comparison tests/hghave.py @ 43076:2372284d9457

formatting: blacken the codebase This is using my patch to black (https://github.com/psf/black/pull/826) so we don't un-wrap collection literals. Done with: hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S # skip-blame mass-reformatting only # no-check-commit reformats foo_bar functions Differential Revision: https://phab.mercurial-scm.org/D6971
author Augie Fackler <augie@google.com>
date Sun, 06 Oct 2019 09:45:02 -0400
parents 07e479ef7c96
children fb41ea2ea076
comparison
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
15 "false": (lambda: False, "nail clipper"), 15 "false": (lambda: False, "nail clipper"),
16 } 16 }
17 17
18 try: 18 try:
19 import msvcrt 19 import msvcrt
20
20 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) 21 msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
21 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY) 22 msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
22 except ImportError: 23 except ImportError:
23 pass 24 pass
24 25
25 stdout = getattr(sys.stdout, 'buffer', sys.stdout) 26 stdout = getattr(sys.stdout, 'buffer', sys.stdout)
26 stderr = getattr(sys.stderr, 'buffer', sys.stderr) 27 stderr = getattr(sys.stderr, 'buffer', sys.stderr)
27 28
28 if sys.version_info[0] >= 3: 29 if sys.version_info[0] >= 3:
30
29 def _bytespath(p): 31 def _bytespath(p):
30 if p is None: 32 if p is None:
31 return p 33 return p
32 return p.encode('utf-8') 34 return p.encode('utf-8')
33 35
34 def _strpath(p): 36 def _strpath(p):
35 if p is None: 37 if p is None:
36 return p 38 return p
37 return p.decode('utf-8') 39 return p.decode('utf-8')
40
41
38 else: 42 else:
43
39 def _bytespath(p): 44 def _bytespath(p):
40 return p 45 return p
41 46
42 _strpath = _bytespath 47 _strpath = _bytespath
43 48
49
44 def check(name, desc): 50 def check(name, desc):
45 """Registers a check function for a feature.""" 51 """Registers a check function for a feature."""
52
46 def decorator(func): 53 def decorator(func):
47 checks[name] = (func, desc) 54 checks[name] = (func, desc)
48 return func 55 return func
56
49 return decorator 57 return decorator
58
50 59
51 def checkvers(name, desc, vers): 60 def checkvers(name, desc, vers):
52 """Registers a check function for each of a series of versions. 61 """Registers a check function for each of a series of versions.
53 62
54 vers can be a list or an iterator""" 63 vers can be a list or an iterator"""
64
55 def decorator(func): 65 def decorator(func):
56 def funcv(v): 66 def funcv(v):
57 def f(): 67 def f():
58 return func(v) 68 return func(v)
69
59 return f 70 return f
71
60 for v in vers: 72 for v in vers:
61 v = str(v) 73 v = str(v)
62 f = funcv(v) 74 f = funcv(v)
63 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v) 75 checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
64 return func 76 return func
77
65 return decorator 78 return decorator
79
66 80
67 def checkfeatures(features): 81 def checkfeatures(features):
68 result = { 82 result = {
69 'error': [], 83 'error': [],
70 'missing': [], 84 'missing': [],
92 elif negate and available: 106 elif negate and available:
93 result['skipped'].append('system supports %s' % desc) 107 result['skipped'].append('system supports %s' % desc)
94 108
95 return result 109 return result
96 110
111
97 def require(features): 112 def require(features):
98 """Require that features are available, exiting if not.""" 113 """Require that features are available, exiting if not."""
99 result = checkfeatures(features) 114 result = checkfeatures(features)
100 115
101 for missing in result['missing']: 116 for missing in result['missing']:
102 stderr.write(('skipped: unknown feature: %s\n' 117 stderr.write(
103 % missing).encode('utf-8')) 118 ('skipped: unknown feature: %s\n' % missing).encode('utf-8')
119 )
104 for msg in result['skipped']: 120 for msg in result['skipped']:
105 stderr.write(('skipped: %s\n' % msg).encode('utf-8')) 121 stderr.write(('skipped: %s\n' % msg).encode('utf-8'))
106 for msg in result['error']: 122 for msg in result['error']:
107 stderr.write(('%s\n' % msg).encode('utf-8')) 123 stderr.write(('%s\n' % msg).encode('utf-8'))
108 124
109 if result['missing']: 125 if result['missing']:
110 sys.exit(2) 126 sys.exit(2)
111 127
112 if result['skipped'] or result['error']: 128 if result['skipped'] or result['error']:
113 sys.exit(1) 129 sys.exit(1)
130
114 131
115 def matchoutput(cmd, regexp, ignorestatus=False): 132 def matchoutput(cmd, regexp, ignorestatus=False):
116 """Return the match object if cmd executes successfully and its output 133 """Return the match object if cmd executes successfully and its output
117 is matched by the supplied regular expression. 134 is matched by the supplied regular expression.
118 """ 135 """
119 r = re.compile(regexp) 136 r = re.compile(regexp)
120 p = subprocess.Popen( 137 p = subprocess.Popen(
121 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 138 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
139 )
122 s = p.communicate()[0] 140 s = p.communicate()[0]
123 ret = p.returncode 141 ret = p.returncode
124 return (ignorestatus or not ret) and r.search(s) 142 return (ignorestatus or not ret) and r.search(s)
125 143
144
126 @check("baz", "GNU Arch baz client") 145 @check("baz", "GNU Arch baz client")
127 def has_baz(): 146 def has_baz():
128 return matchoutput('baz --version 2>&1', br'baz Bazaar version') 147 return matchoutput('baz --version 2>&1', br'baz Bazaar version')
148
129 149
130 @check("bzr", "Canonical's Bazaar client") 150 @check("bzr", "Canonical's Bazaar client")
131 def has_bzr(): 151 def has_bzr():
132 try: 152 try:
133 import bzrlib 153 import bzrlib
134 import bzrlib.bzrdir 154 import bzrlib.bzrdir
135 import bzrlib.errors 155 import bzrlib.errors
136 import bzrlib.revision 156 import bzrlib.revision
137 import bzrlib.revisionspec 157 import bzrlib.revisionspec
158
138 bzrlib.revisionspec.RevisionSpec 159 bzrlib.revisionspec.RevisionSpec
139 return bzrlib.__doc__ is not None 160 return bzrlib.__doc__ is not None
140 except (AttributeError, ImportError): 161 except (AttributeError, ImportError):
141 return False 162 return False
142 163
164
143 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,)) 165 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
144 def has_bzr_range(v): 166 def has_bzr_range(v):
145 major, minor = v.split('rc')[0].split('.')[0:2] 167 major, minor = v.split('rc')[0].split('.')[0:2]
146 try: 168 try:
147 import bzrlib 169 import bzrlib
148 return (bzrlib.__doc__ is not None 170
149 and bzrlib.version_info[:2] >= (int(major), int(minor))) 171 return bzrlib.__doc__ is not None and bzrlib.version_info[:2] >= (
150 except ImportError: 172 int(major),
151 return False 173 int(minor),
174 )
175 except ImportError:
176 return False
177
152 178
153 @check("chg", "running with chg") 179 @check("chg", "running with chg")
154 def has_chg(): 180 def has_chg():
155 return 'CHGHG' in os.environ 181 return 'CHGHG' in os.environ
182
156 183
157 @check("cvs", "cvs client/server") 184 @check("cvs", "cvs client/server")
158 def has_cvs(): 185 def has_cvs():
159 re = br'Concurrent Versions System.*?server' 186 re = br'Concurrent Versions System.*?server'
160 return matchoutput('cvs --version 2>&1', re) and not has_msys() 187 return matchoutput('cvs --version 2>&1', re) and not has_msys()
161 188
189
162 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)") 190 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
163 def has_cvs112(): 191 def has_cvs112():
164 re = br'Concurrent Versions System \(CVS\) 1.12.*?server' 192 re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
165 return matchoutput('cvs --version 2>&1', re) and not has_msys() 193 return matchoutput('cvs --version 2>&1', re) and not has_msys()
166 194
195
167 @check("cvsnt", "cvsnt client/server") 196 @check("cvsnt", "cvsnt client/server")
168 def has_cvsnt(): 197 def has_cvsnt():
169 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)' 198 re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
170 return matchoutput('cvsnt --version 2>&1', re) 199 return matchoutput('cvsnt --version 2>&1', re)
171 200
201
172 @check("darcs", "darcs client") 202 @check("darcs", "darcs client")
173 def has_darcs(): 203 def has_darcs():
174 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True) 204 return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
175 205
206
176 @check("mtn", "monotone client (>= 1.0)") 207 @check("mtn", "monotone client (>= 1.0)")
177 def has_mtn(): 208 def has_mtn():
178 return matchoutput('mtn --version', br'monotone', True) and not matchoutput( 209 return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
179 'mtn --version', br'monotone 0\.', True) 210 'mtn --version', br'monotone 0\.', True
211 )
212
180 213
181 @check("eol-in-paths", "end-of-lines in paths") 214 @check("eol-in-paths", "end-of-lines in paths")
182 def has_eol_in_paths(): 215 def has_eol_in_paths():
183 try: 216 try:
184 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r') 217 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix, suffix='\n\r')
185 os.close(fd) 218 os.close(fd)
186 os.remove(path) 219 os.remove(path)
187 return True 220 return True
188 except (IOError, OSError): 221 except (IOError, OSError):
189 return False 222 return False
223
190 224
191 @check("execbit", "executable bit") 225 @check("execbit", "executable bit")
192 def has_executablebit(): 226 def has_executablebit():
193 try: 227 try:
194 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH 228 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
196 try: 230 try:
197 os.close(fh) 231 os.close(fh)
198 m = os.stat(fn).st_mode & 0o777 232 m = os.stat(fn).st_mode & 0o777
199 new_file_has_exec = m & EXECFLAGS 233 new_file_has_exec = m & EXECFLAGS
200 os.chmod(fn, m ^ EXECFLAGS) 234 os.chmod(fn, m ^ EXECFLAGS)
201 exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m) 235 exec_flags_cannot_flip = (os.stat(fn).st_mode & 0o777) == m
202 finally: 236 finally:
203 os.unlink(fn) 237 os.unlink(fn)
204 except (IOError, OSError): 238 except (IOError, OSError):
205 # we don't care, the user probably won't be able to commit anyway 239 # we don't care, the user probably won't be able to commit anyway
206 return False 240 return False
207 return not (new_file_has_exec or exec_flags_cannot_flip) 241 return not (new_file_has_exec or exec_flags_cannot_flip)
242
208 243
209 @check("icasefs", "case insensitive file system") 244 @check("icasefs", "case insensitive file system")
210 def has_icasefs(): 245 def has_icasefs():
211 # Stolen from mercurial.util 246 # Stolen from mercurial.util
212 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix) 247 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
223 except OSError: 258 except OSError:
224 return False 259 return False
225 finally: 260 finally:
226 os.remove(path) 261 os.remove(path)
227 262
263
228 @check("fifo", "named pipes") 264 @check("fifo", "named pipes")
229 def has_fifo(): 265 def has_fifo():
230 if getattr(os, "mkfifo", None) is None: 266 if getattr(os, "mkfifo", None) is None:
231 return False 267 return False
232 name = tempfile.mktemp(dir='.', prefix=tempprefix) 268 name = tempfile.mktemp(dir='.', prefix=tempprefix)
235 os.unlink(name) 271 os.unlink(name)
236 return True 272 return True
237 except OSError: 273 except OSError:
238 return False 274 return False
239 275
276
240 @check("killdaemons", 'killdaemons.py support') 277 @check("killdaemons", 'killdaemons.py support')
241 def has_killdaemons(): 278 def has_killdaemons():
242 return True 279 return True
243 280
281
244 @check("cacheable", "cacheable filesystem") 282 @check("cacheable", "cacheable filesystem")
245 def has_cacheable_fs(): 283 def has_cacheable_fs():
246 from mercurial import util 284 from mercurial import util
247 285
248 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix) 286 fd, path = tempfile.mkstemp(dir='.', prefix=tempprefix)
250 try: 288 try:
251 return util.cachestat(path).cacheable() 289 return util.cachestat(path).cacheable()
252 finally: 290 finally:
253 os.remove(path) 291 os.remove(path)
254 292
293
255 @check("lsprof", "python lsprof module") 294 @check("lsprof", "python lsprof module")
256 def has_lsprof(): 295 def has_lsprof():
257 try: 296 try:
258 import _lsprof 297 import _lsprof
259 _lsprof.Profiler # silence unused import warning 298
260 return True 299 _lsprof.Profiler # silence unused import warning
261 except ImportError: 300 return True
262 return False 301 except ImportError:
302 return False
303
263 304
264 def gethgversion(): 305 def gethgversion():
265 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)') 306 m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
266 if not m: 307 if not m:
267 return (0, 0) 308 return (0, 0)
268 return (int(m.group(1)), int(m.group(2))) 309 return (int(m.group(1)), int(m.group(2)))
269 310
270 @checkvers("hg", "Mercurial >= %s", 311
271 list([(1.0 * x) / 10 for x in range(9, 99)])) 312 @checkvers(
313 "hg", "Mercurial >= %s", list([(1.0 * x) / 10 for x in range(9, 99)])
314 )
272 def has_hg_range(v): 315 def has_hg_range(v):
273 major, minor = v.split('.')[0:2] 316 major, minor = v.split('.')[0:2]
274 return gethgversion() >= (int(major), int(minor)) 317 return gethgversion() >= (int(major), int(minor))
275 318
319
276 @check("hg08", "Mercurial >= 0.8") 320 @check("hg08", "Mercurial >= 0.8")
277 def has_hg08(): 321 def has_hg08():
278 if checks["hg09"][0](): 322 if checks["hg09"][0]():
279 return True 323 return True
280 return matchoutput('hg help annotate 2>&1', '--date') 324 return matchoutput('hg help annotate 2>&1', '--date')
281 325
326
282 @check("hg07", "Mercurial >= 0.7") 327 @check("hg07", "Mercurial >= 0.7")
283 def has_hg07(): 328 def has_hg07():
284 if checks["hg08"][0](): 329 if checks["hg08"][0]():
285 return True 330 return True
286 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM') 331 return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
287 332
333
288 @check("hg06", "Mercurial >= 0.6") 334 @check("hg06", "Mercurial >= 0.6")
289 def has_hg06(): 335 def has_hg06():
290 if checks["hg07"][0](): 336 if checks["hg07"][0]():
291 return True 337 return True
292 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version') 338 return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
293 339
340
294 @check("gettext", "GNU Gettext (msgfmt)") 341 @check("gettext", "GNU Gettext (msgfmt)")
295 def has_gettext(): 342 def has_gettext():
296 return matchoutput('msgfmt --version', br'GNU gettext-tools') 343 return matchoutput('msgfmt --version', br'GNU gettext-tools')
297 344
345
298 @check("git", "git command line client") 346 @check("git", "git command line client")
299 def has_git(): 347 def has_git():
300 return matchoutput('git --version 2>&1', br'^git version') 348 return matchoutput('git --version 2>&1', br'^git version')
349
301 350
302 def getgitversion(): 351 def getgitversion():
303 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)') 352 m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
304 if not m: 353 if not m:
305 return (0, 0) 354 return (0, 0)
306 return (int(m.group(1)), int(m.group(2))) 355 return (int(m.group(1)), int(m.group(2)))
356
307 357
308 # https://github.com/git-lfs/lfs-test-server 358 # https://github.com/git-lfs/lfs-test-server
309 @check("lfs-test-server", "git-lfs test server") 359 @check("lfs-test-server", "git-lfs test server")
310 def has_lfsserver(): 360 def has_lfsserver():
311 exe = 'lfs-test-server' 361 exe = 'lfs-test-server'
314 return any( 364 return any(
315 os.access(os.path.join(path, exe), os.X_OK) 365 os.access(os.path.join(path, exe), os.X_OK)
316 for path in os.environ["PATH"].split(os.pathsep) 366 for path in os.environ["PATH"].split(os.pathsep)
317 ) 367 )
318 368
369
319 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,)) 370 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
320 def has_git_range(v): 371 def has_git_range(v):
321 major, minor = v.split('.')[0:2] 372 major, minor = v.split('.')[0:2]
322 return getgitversion() >= (int(major), int(minor)) 373 return getgitversion() >= (int(major), int(minor))
323 374
375
324 @check("docutils", "Docutils text processing library") 376 @check("docutils", "Docutils text processing library")
325 def has_docutils(): 377 def has_docutils():
326 try: 378 try:
327 import docutils.core 379 import docutils.core
328 docutils.core.publish_cmdline # silence unused import 380
329 return True 381 docutils.core.publish_cmdline # silence unused import
330 except ImportError: 382 return True
331 return False 383 except ImportError:
384 return False
385
332 386
333 def getsvnversion(): 387 def getsvnversion():
334 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)') 388 m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
335 if not m: 389 if not m:
336 return (0, 0) 390 return (0, 0)
337 return (int(m.group(1)), int(m.group(2))) 391 return (int(m.group(1)), int(m.group(2)))
338 392
393
339 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5)) 394 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
340 def has_svn_range(v): 395 def has_svn_range(v):
341 major, minor = v.split('.')[0:2] 396 major, minor = v.split('.')[0:2]
342 return getsvnversion() >= (int(major), int(minor)) 397 return getsvnversion() >= (int(major), int(minor))
343 398
399
344 @check("svn", "subversion client and admin tools") 400 @check("svn", "subversion client and admin tools")
345 def has_svn(): 401 def has_svn():
346 return (matchoutput('svn --version 2>&1', br'^svn, version') and 402 return matchoutput('svn --version 2>&1', br'^svn, version') and matchoutput(
347 matchoutput('svnadmin --version 2>&1', br'^svnadmin, version')) 403 'svnadmin --version 2>&1', br'^svnadmin, version'
404 )
405
348 406
349 @check("svn-bindings", "subversion python bindings") 407 @check("svn-bindings", "subversion python bindings")
350 def has_svn_bindings(): 408 def has_svn_bindings():
351 try: 409 try:
352 import svn.core 410 import svn.core
411
353 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR 412 version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
354 if version < (1, 4): 413 if version < (1, 4):
355 return False 414 return False
356 return True 415 return True
357 except ImportError: 416 except ImportError:
358 return False 417 return False
359 418
419
360 @check("p4", "Perforce server and client") 420 @check("p4", "Perforce server and client")
361 def has_p4(): 421 def has_p4():
362 return (matchoutput('p4 -V', br'Rev\. P4/') and 422 return matchoutput('p4 -V', br'Rev\. P4/') and matchoutput(
363 matchoutput('p4d -V', br'Rev\. P4D/')) 423 'p4d -V', br'Rev\. P4D/'
424 )
425
364 426
365 @check("symlink", "symbolic links") 427 @check("symlink", "symbolic links")
366 def has_symlink(): 428 def has_symlink():
367 if getattr(os, "symlink", None) is None: 429 if getattr(os, "symlink", None) is None:
368 return False 430 return False
372 os.unlink(name) 434 os.unlink(name)
373 return True 435 return True
374 except (OSError, AttributeError): 436 except (OSError, AttributeError):
375 return False 437 return False
376 438
439
377 @check("hardlink", "hardlinks") 440 @check("hardlink", "hardlinks")
378 def has_hardlink(): 441 def has_hardlink():
379 from mercurial import util 442 from mercurial import util
443
380 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix) 444 fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
381 os.close(fh) 445 os.close(fh)
382 name = tempfile.mktemp(dir='.', prefix=tempprefix) 446 name = tempfile.mktemp(dir='.', prefix=tempprefix)
383 try: 447 try:
384 util.oslink(_bytespath(fn), _bytespath(name)) 448 util.oslink(_bytespath(fn), _bytespath(name))
387 except OSError: 451 except OSError:
388 return False 452 return False
389 finally: 453 finally:
390 os.unlink(fn) 454 os.unlink(fn)
391 455
456
392 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems") 457 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
393 def has_hardlink_whitelisted(): 458 def has_hardlink_whitelisted():
394 from mercurial import util 459 from mercurial import util
460
395 try: 461 try:
396 fstype = util.getfstype(b'.') 462 fstype = util.getfstype(b'.')
397 except OSError: 463 except OSError:
398 return False 464 return False
399 return fstype in util._hardlinkfswhitelist 465 return fstype in util._hardlinkfswhitelist
466
400 467
401 @check("rmcwd", "can remove current working directory") 468 @check("rmcwd", "can remove current working directory")
402 def has_rmcwd(): 469 def has_rmcwd():
403 ocwd = os.getcwd() 470 ocwd = os.getcwd()
404 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix) 471 temp = tempfile.mkdtemp(dir='.', prefix=tempprefix)
416 try: 483 try:
417 os.rmdir(temp) 484 os.rmdir(temp)
418 except OSError: 485 except OSError:
419 pass 486 pass
420 487
488
421 @check("tla", "GNU Arch tla client") 489 @check("tla", "GNU Arch tla client")
422 def has_tla(): 490 def has_tla():
423 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision') 491 return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
424 492
493
425 @check("gpg", "gpg client") 494 @check("gpg", "gpg client")
426 def has_gpg(): 495 def has_gpg():
427 return matchoutput('gpg --version 2>&1', br'GnuPG') 496 return matchoutput('gpg --version 2>&1', br'GnuPG')
428 497
498
429 @check("gpg2", "gpg client v2") 499 @check("gpg2", "gpg client v2")
430 def has_gpg2(): 500 def has_gpg2():
431 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.') 501 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
432 502
503
433 @check("gpg21", "gpg client v2.1+") 504 @check("gpg21", "gpg client v2.1+")
434 def has_gpg21(): 505 def has_gpg21():
435 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)') 506 return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
507
436 508
437 @check("unix-permissions", "unix-style permissions") 509 @check("unix-permissions", "unix-style permissions")
438 def has_unix_permissions(): 510 def has_unix_permissions():
439 d = tempfile.mkdtemp(dir='.', prefix=tempprefix) 511 d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
440 try: 512 try:
449 return False 521 return False
450 return True 522 return True
451 finally: 523 finally:
452 os.rmdir(d) 524 os.rmdir(d)
453 525
526
454 @check("unix-socket", "AF_UNIX socket family") 527 @check("unix-socket", "AF_UNIX socket family")
455 def has_unix_socket(): 528 def has_unix_socket():
456 return getattr(socket, 'AF_UNIX', None) is not None 529 return getattr(socket, 'AF_UNIX', None) is not None
457 530
531
458 @check("root", "root permissions") 532 @check("root", "root permissions")
459 def has_root(): 533 def has_root():
460 return getattr(os, 'geteuid', None) and os.geteuid() == 0 534 return getattr(os, 'geteuid', None) and os.geteuid() == 0
461 535
536
462 @check("pyflakes", "Pyflakes python linter") 537 @check("pyflakes", "Pyflakes python linter")
463 def has_pyflakes(): 538 def has_pyflakes():
464 return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"", 539 return matchoutput(
465 br"<stdin>:1: 're' imported but unused", 540 "sh -c \"echo 'import re' 2>&1 | pyflakes\"",
466 True) 541 br"<stdin>:1: 're' imported but unused",
542 True,
543 )
544
467 545
468 @check("pylint", "Pylint python linter") 546 @check("pylint", "Pylint python linter")
469 def has_pylint(): 547 def has_pylint():
470 return matchoutput("pylint --help", 548 return matchoutput("pylint --help", br"Usage: pylint", True)
471 br"Usage: pylint", 549
472 True)
473 550
474 @check("clang-format", "clang-format C code formatter") 551 @check("clang-format", "clang-format C code formatter")
475 def has_clang_format(): 552 def has_clang_format():
476 m = matchoutput('clang-format --version', br'clang-format version (\d)') 553 m = matchoutput('clang-format --version', br'clang-format version (\d)')
477 # style changed somewhere between 4.x and 6.x 554 # style changed somewhere between 4.x and 6.x
478 return m and int(m.group(1)) >= 6 555 return m and int(m.group(1)) >= 6
479 556
557
480 @check("jshint", "JSHint static code analysis tool") 558 @check("jshint", "JSHint static code analysis tool")
481 def has_jshint(): 559 def has_jshint():
482 return matchoutput("jshint --version 2>&1", br"jshint v") 560 return matchoutput("jshint --version 2>&1", br"jshint v")
483 561
562
484 @check("pygments", "Pygments source highlighting library") 563 @check("pygments", "Pygments source highlighting library")
485 def has_pygments(): 564 def has_pygments():
486 try: 565 try:
487 import pygments 566 import pygments
488 pygments.highlight # silence unused import warning 567
489 return True 568 pygments.highlight # silence unused import warning
490 except ImportError: 569 return True
491 return False 570 except ImportError:
571 return False
572
492 573
493 @check("outer-repo", "outer repo") 574 @check("outer-repo", "outer repo")
494 def has_outer_repo(): 575 def has_outer_repo():
495 # failing for other reasons than 'no repo' imply that there is a repo 576 # failing for other reasons than 'no repo' imply that there is a repo
496 return not matchoutput('hg root 2>&1', 577 return not matchoutput('hg root 2>&1', br'abort: no repository found', True)
497 br'abort: no repository found', True) 578
498 579
499 @check("ssl", "ssl module available") 580 @check("ssl", "ssl module available")
500 def has_ssl(): 581 def has_ssl():
501 try: 582 try:
502 import ssl 583 import ssl
584
503 ssl.CERT_NONE 585 ssl.CERT_NONE
504 return True 586 return True
505 except ImportError: 587 except ImportError:
506 return False 588 return False
589
507 590
508 @check("sslcontext", "python >= 2.7.9 ssl") 591 @check("sslcontext", "python >= 2.7.9 ssl")
509 def has_sslcontext(): 592 def has_sslcontext():
510 try: 593 try:
511 import ssl 594 import ssl
595
512 ssl.SSLContext 596 ssl.SSLContext
513 return True 597 return True
514 except (ImportError, AttributeError): 598 except (ImportError, AttributeError):
515 return False 599 return False
600
516 601
517 @check("defaultcacerts", "can verify SSL certs by system's CA certs store") 602 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
518 def has_defaultcacerts(): 603 def has_defaultcacerts():
519 from mercurial import sslutil, ui as uimod 604 from mercurial import sslutil, ui as uimod
605
520 ui = uimod.ui.load() 606 ui = uimod.ui.load()
521 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts 607 return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
608
522 609
523 @check("defaultcacertsloaded", "detected presence of loaded system CA certs") 610 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
524 def has_defaultcacertsloaded(): 611 def has_defaultcacertsloaded():
525 import ssl 612 import ssl
526 from mercurial import sslutil, ui as uimod 613 from mercurial import sslutil, ui as uimod
538 else: 625 else:
539 ctx.load_default_certs() 626 ctx.load_default_certs()
540 627
541 return len(ctx.get_ca_certs()) > 0 628 return len(ctx.get_ca_certs()) > 0
542 629
630
543 @check("tls1.2", "TLS 1.2 protocol support") 631 @check("tls1.2", "TLS 1.2 protocol support")
544 def has_tls1_2(): 632 def has_tls1_2():
545 from mercurial import sslutil 633 from mercurial import sslutil
634
546 return b'tls1.2' in sslutil.supportedprotocols 635 return b'tls1.2' in sslutil.supportedprotocols
636
547 637
548 @check("windows", "Windows") 638 @check("windows", "Windows")
549 def has_windows(): 639 def has_windows():
550 return os.name == 'nt' 640 return os.name == 'nt'
551 641
642
552 @check("system-sh", "system() uses sh") 643 @check("system-sh", "system() uses sh")
553 def has_system_sh(): 644 def has_system_sh():
554 return os.name != 'nt' 645 return os.name != 'nt'
555 646
647
556 @check("serve", "platform and python can manage 'hg serve -d'") 648 @check("serve", "platform and python can manage 'hg serve -d'")
557 def has_serve(): 649 def has_serve():
558 return True 650 return True
651
559 652
560 @check("test-repo", "running tests from repository") 653 @check("test-repo", "running tests from repository")
561 def has_test_repo(): 654 def has_test_repo():
562 t = os.environ["TESTDIR"] 655 t = os.environ["TESTDIR"]
563 return os.path.isdir(os.path.join(t, "..", ".hg")) 656 return os.path.isdir(os.path.join(t, "..", ".hg"))
564 657
658
565 @check("tic", "terminfo compiler and curses module") 659 @check("tic", "terminfo compiler and curses module")
566 def has_tic(): 660 def has_tic():
567 try: 661 try:
568 import curses 662 import curses
663
569 curses.COLOR_BLUE 664 curses.COLOR_BLUE
570 return matchoutput('test -x "`which tic`"', br'') 665 return matchoutput('test -x "`which tic`"', br'')
571 except ImportError: 666 except ImportError:
572 return False 667 return False
668
573 669
574 @check("msys", "Windows with MSYS") 670 @check("msys", "Windows with MSYS")
575 def has_msys(): 671 def has_msys():
576 return os.getenv('MSYSTEM') 672 return os.getenv('MSYSTEM')
577 673
674
578 @check("aix", "AIX") 675 @check("aix", "AIX")
579 def has_aix(): 676 def has_aix():
580 return sys.platform.startswith("aix") 677 return sys.platform.startswith("aix")
581 678
679
582 @check("osx", "OS X") 680 @check("osx", "OS X")
583 def has_osx(): 681 def has_osx():
584 return sys.platform == 'darwin' 682 return sys.platform == 'darwin'
585 683
684
586 @check("osxpackaging", "OS X packaging tools") 685 @check("osxpackaging", "OS X packaging tools")
587 def has_osxpackaging(): 686 def has_osxpackaging():
588 try: 687 try:
589 return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1) 688 return (
590 and matchoutput( 689 matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
591 'productbuild', br'Usage: productbuild ', 690 and matchoutput(
592 ignorestatus=1) 691 'productbuild', br'Usage: productbuild ', ignorestatus=1
593 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1) 692 )
594 and matchoutput( 693 and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
595 'xar --help', br'Usage: xar', ignorestatus=1)) 694 and matchoutput('xar --help', br'Usage: xar', ignorestatus=1)
596 except ImportError: 695 )
597 return False 696 except ImportError:
697 return False
698
598 699
599 @check('linuxormacos', 'Linux or MacOS') 700 @check('linuxormacos', 'Linux or MacOS')
600 def has_linuxormacos(): 701 def has_linuxormacos():
601 # This isn't a perfect test for MacOS. But it is sufficient for our needs. 702 # This isn't a perfect test for MacOS. But it is sufficient for our needs.
602 return sys.platform.startswith(('linux', 'darwin')) 703 return sys.platform.startswith(('linux', 'darwin'))
704
603 705
604 @check("docker", "docker support") 706 @check("docker", "docker support")
605 def has_docker(): 707 def has_docker():
606 pat = br'A self-sufficient runtime for' 708 pat = br'A self-sufficient runtime for'
607 if matchoutput('docker --help', pat): 709 if matchoutput('docker --help', pat):
616 return False 718 return False
617 719
618 return True 720 return True
619 return False 721 return False
620 722
723
621 @check("debhelper", "debian packaging tools") 724 @check("debhelper", "debian packaging tools")
622 def has_debhelper(): 725 def has_debhelper():
623 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first 726 # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
624 # quote), so just accept anything in that spot. 727 # quote), so just accept anything in that spot.
625 dpkg = matchoutput('dpkg --version', 728 dpkg = matchoutput(
626 br"Debian .dpkg' package management program") 729 'dpkg --version', br"Debian .dpkg' package management program"
627 dh = matchoutput('dh --help', 730 )
628 br'dh is a part of debhelper.', ignorestatus=True) 731 dh = matchoutput(
629 dh_py2 = matchoutput('dh_python2 --help', 732 'dh --help', br'dh is a part of debhelper.', ignorestatus=True
630 br'other supported Python versions') 733 )
734 dh_py2 = matchoutput(
735 'dh_python2 --help', br'other supported Python versions'
736 )
631 # debuild comes from the 'devscripts' package, though you might want 737 # debuild comes from the 'devscripts' package, though you might want
632 # the 'build-debs' package instead, which has a dependency on devscripts. 738 # the 'build-debs' package instead, which has a dependency on devscripts.
633 debuild = matchoutput('debuild --help', 739 debuild = matchoutput(
634 br'to run debian/rules with given parameter') 740 'debuild --help', br'to run debian/rules with given parameter'
741 )
635 return dpkg and dh and dh_py2 and debuild 742 return dpkg and dh and dh_py2 and debuild
636 743
637 @check("debdeps", 744
638 "debian build dependencies (run dpkg-checkbuilddeps in contrib/)") 745 @check(
746 "debdeps", "debian build dependencies (run dpkg-checkbuilddeps in contrib/)"
747 )
639 def has_debdeps(): 748 def has_debdeps():
640 # just check exit status (ignoring output) 749 # just check exit status (ignoring output)
641 path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR'] 750 path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR']
642 return matchoutput('dpkg-checkbuilddeps %s' % path, br'') 751 return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
643 752
753
644 @check("demandimport", "demandimport enabled") 754 @check("demandimport", "demandimport enabled")
645 def has_demandimport(): 755 def has_demandimport():
646 # chg disables demandimport intentionally for performance wins. 756 # chg disables demandimport intentionally for performance wins.
647 return ((not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable') 757 return (not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable'
758
648 759
649 @checkvers("py", "Python >= %s", (2.7, 3.5, 3.6, 3.7, 3.8, 3.9)) 760 @checkvers("py", "Python >= %s", (2.7, 3.5, 3.6, 3.7, 3.8, 3.9))
650 def has_python_range(v): 761 def has_python_range(v):
651 major, minor = v.split('.')[0:2] 762 major, minor = v.split('.')[0:2]
652 py_major, py_minor = sys.version_info.major, sys.version_info.minor 763 py_major, py_minor = sys.version_info.major, sys.version_info.minor
653 764
654 return (py_major, py_minor) >= (int(major), int(minor)) 765 return (py_major, py_minor) >= (int(major), int(minor))
655 766
767
656 @check("py3", "running with Python 3.x") 768 @check("py3", "running with Python 3.x")
657 def has_py3(): 769 def has_py3():
658 return 3 == sys.version_info[0] 770 return 3 == sys.version_info[0]
659 771
772
660 @check("py3exe", "a Python 3.x interpreter is available") 773 @check("py3exe", "a Python 3.x interpreter is available")
661 def has_python3exe(): 774 def has_python3exe():
662 return matchoutput('python3 -V', br'^Python 3.(5|6|7|8|9)') 775 return matchoutput('python3 -V', br'^Python 3.(5|6|7|8|9)')
663 776
777
664 @check("pure", "running with pure Python code") 778 @check("pure", "running with pure Python code")
665 def has_pure(): 779 def has_pure():
666 return any([ 780 return any(
667 os.environ.get("HGMODULEPOLICY") == "py", 781 [
668 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure", 782 os.environ.get("HGMODULEPOLICY") == "py",
669 ]) 783 os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
784 ]
785 )
786
670 787
671 @check("slow", "allow slow tests (use --allow-slow-tests)") 788 @check("slow", "allow slow tests (use --allow-slow-tests)")
672 def has_slow(): 789 def has_slow():
673 return os.environ.get('HGTEST_SLOW') == 'slow' 790 return os.environ.get('HGTEST_SLOW') == 'slow'
674 791
792
675 @check("hypothesis", "Hypothesis automated test generation") 793 @check("hypothesis", "Hypothesis automated test generation")
676 def has_hypothesis(): 794 def has_hypothesis():
677 try: 795 try:
678 import hypothesis 796 import hypothesis
797
679 hypothesis.given 798 hypothesis.given
680 return True 799 return True
681 except ImportError: 800 except ImportError:
682 return False 801 return False
802
683 803
684 @check("unziplinks", "unzip(1) understands and extracts symlinks") 804 @check("unziplinks", "unzip(1) understands and extracts symlinks")
685 def unzip_understands_symlinks(): 805 def unzip_understands_symlinks():
686 return matchoutput('unzip --help', br'Info-ZIP') 806 return matchoutput('unzip --help', br'Info-ZIP')
687 807
808
688 @check("zstd", "zstd Python module available") 809 @check("zstd", "zstd Python module available")
689 def has_zstd(): 810 def has_zstd():
690 try: 811 try:
691 import mercurial.zstd 812 import mercurial.zstd
813
692 mercurial.zstd.__version__ 814 mercurial.zstd.__version__
693 return True 815 return True
694 except ImportError: 816 except ImportError:
695 return False 817 return False
818
696 819
697 @check("devfull", "/dev/full special file") 820 @check("devfull", "/dev/full special file")
698 def has_dev_full(): 821 def has_dev_full():
699 return os.path.exists('/dev/full') 822 return os.path.exists('/dev/full')
700 823
824
701 @check("virtualenv", "Python virtualenv support") 825 @check("virtualenv", "Python virtualenv support")
702 def has_virtualenv(): 826 def has_virtualenv():
703 try: 827 try:
704 import virtualenv 828 import virtualenv
829
705 virtualenv.ACTIVATE_SH 830 virtualenv.ACTIVATE_SH
706 return True 831 return True
707 except ImportError: 832 except ImportError:
708 return False 833 return False
834
709 835
710 @check("fsmonitor", "running tests with fsmonitor") 836 @check("fsmonitor", "running tests with fsmonitor")
711 def has_fsmonitor(): 837 def has_fsmonitor():
712 return 'HGFSMONITOR_TESTS' in os.environ 838 return 'HGFSMONITOR_TESTS' in os.environ
713 839
840
714 @check("fuzzywuzzy", "Fuzzy string matching library") 841 @check("fuzzywuzzy", "Fuzzy string matching library")
715 def has_fuzzywuzzy(): 842 def has_fuzzywuzzy():
716 try: 843 try:
717 import fuzzywuzzy 844 import fuzzywuzzy
845
718 fuzzywuzzy.__version__ 846 fuzzywuzzy.__version__
719 return True 847 return True
720 except ImportError: 848 except ImportError:
721 return False 849 return False
850
722 851
723 @check("clang-libfuzzer", "clang new enough to include libfuzzer") 852 @check("clang-libfuzzer", "clang new enough to include libfuzzer")
724 def has_clang_libfuzzer(): 853 def has_clang_libfuzzer():
725 mat = matchoutput('clang --version', br'clang version (\d)') 854 mat = matchoutput('clang --version', br'clang version (\d)')
726 if mat: 855 if mat:
727 # libfuzzer is new in clang 6 856 # libfuzzer is new in clang 6
728 return int(mat.group(1)) > 5 857 return int(mat.group(1)) > 5
729 return False 858 return False
730 859
860
731 @check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)") 861 @check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)")
732 def has_clang60(): 862 def has_clang60():
733 return matchoutput('clang-6.0 --version', br'clang version 6\.') 863 return matchoutput('clang-6.0 --version', br'clang version 6\.')
734 864
865
735 @check("xdiff", "xdiff algorithm") 866 @check("xdiff", "xdiff algorithm")
736 def has_xdiff(): 867 def has_xdiff():
737 try: 868 try:
738 from mercurial import policy 869 from mercurial import policy
870
739 bdiff = policy.importmod('bdiff') 871 bdiff = policy.importmod('bdiff')
740 return bdiff.xdiffblocks(b'', b'') == [(0, 0, 0, 0)] 872 return bdiff.xdiffblocks(b'', b'') == [(0, 0, 0, 0)]
741 except (ImportError, AttributeError): 873 except (ImportError, AttributeError):
742 return False 874 return False
743 875
876
744 @check('extraextensions', 'whether tests are running with extra extensions') 877 @check('extraextensions', 'whether tests are running with extra extensions')
745 def has_extraextensions(): 878 def has_extraextensions():
746 return 'HGTESTEXTRAEXTENSIONS' in os.environ 879 return 'HGTESTEXTRAEXTENSIONS' in os.environ
880
747 881
748 def getrepofeatures(): 882 def getrepofeatures():
749 """Obtain set of repository features in use. 883 """Obtain set of repository features in use.
750 884
751 HGREPOFEATURES can be used to define or remove features. It contains 885 HGREPOFEATURES can be used to define or remove features. It contains
781 else: 915 else:
782 features.add(imply) 916 features.add(imply)
783 917
784 return features 918 return features
785 919
920
786 @check('reporevlogstore', 'repository using the default revlog store') 921 @check('reporevlogstore', 'repository using the default revlog store')
787 def has_reporevlogstore(): 922 def has_reporevlogstore():
788 return 'revlogstore' in getrepofeatures() 923 return 'revlogstore' in getrepofeatures()
789 924
925
790 @check('reposimplestore', 'repository using simple storage extension') 926 @check('reposimplestore', 'repository using simple storage extension')
791 def has_reposimplestore(): 927 def has_reposimplestore():
792 return 'simplestore' in getrepofeatures() 928 return 'simplestore' in getrepofeatures()
793 929
930
794 @check('repobundlerepo', 'whether we can open bundle files as repos') 931 @check('repobundlerepo', 'whether we can open bundle files as repos')
795 def has_repobundlerepo(): 932 def has_repobundlerepo():
796 return 'bundlerepo' in getrepofeatures() 933 return 'bundlerepo' in getrepofeatures()
797 934
935
798 @check('repofncache', 'repository has an fncache') 936 @check('repofncache', 'repository has an fncache')
799 def has_repofncache(): 937 def has_repofncache():
800 return 'fncache' in getrepofeatures() 938 return 'fncache' in getrepofeatures()
801 939
940
802 @check('sqlite', 'sqlite3 module is available') 941 @check('sqlite', 'sqlite3 module is available')
803 def has_sqlite(): 942 def has_sqlite():
804 try: 943 try:
805 import sqlite3 944 import sqlite3
945
806 version = sqlite3.sqlite_version_info 946 version = sqlite3.sqlite_version_info
807 except ImportError: 947 except ImportError:
808 return False 948 return False
809 949
810 if version < (3, 8, 3): 950 if version < (3, 8, 3):
811 # WITH clause not supported 951 # WITH clause not supported
812 return False 952 return False
813 953
814 return matchoutput('sqlite3 -version', br'^3\.\d+') 954 return matchoutput('sqlite3 -version', br'^3\.\d+')
815 955
956
816 @check('vcr', 'vcr http mocking library') 957 @check('vcr', 'vcr http mocking library')
817 def has_vcr(): 958 def has_vcr():
818 try: 959 try:
819 import vcr 960 import vcr
961
820 vcr.VCR 962 vcr.VCR
821 return True 963 return True
822 except (ImportError, AttributeError): 964 except (ImportError, AttributeError):
823 pass 965 pass
824 return False 966 return False
967
825 968
826 @check('emacs', 'GNU Emacs') 969 @check('emacs', 'GNU Emacs')
827 def has_emacs(): 970 def has_emacs():
828 # Our emacs lisp uses `with-eval-after-load` which is new in emacs 971 # Our emacs lisp uses `with-eval-after-load` which is new in emacs
829 # 24.4, so we allow emacs 24.4, 24.5, and 25+ (24.5 was the last 972 # 24.4, so we allow emacs 24.4, 24.5, and 25+ (24.5 was the last