tests/hghave.py
changeset 43076 2372284d9457
parent 42215 07e479ef7c96
child 43095 fb41ea2ea076
--- a/tests/hghave.py	Sat Oct 05 10:29:34 2019 -0400
+++ b/tests/hghave.py	Sun Oct 06 09:45:02 2019 -0400
@@ -17,6 +17,7 @@
 
 try:
     import msvcrt
+
     msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
     msvcrt.setmode(sys.stderr.fileno(), os.O_BINARY)
 except ImportError:
@@ -26,6 +27,7 @@
 stderr = getattr(sys.stderr, 'buffer', sys.stderr)
 
 if sys.version_info[0] >= 3:
+
     def _bytespath(p):
         if p is None:
             return p
@@ -35,35 +37,47 @@
         if p is None:
             return p
         return p.decode('utf-8')
+
+
 else:
+
     def _bytespath(p):
         return p
 
     _strpath = _bytespath
 
+
 def check(name, desc):
     """Registers a check function for a feature."""
+
     def decorator(func):
         checks[name] = (func, desc)
         return func
+
     return decorator
 
+
 def checkvers(name, desc, vers):
     """Registers a check function for each of a series of versions.
 
     vers can be a list or an iterator"""
+
     def decorator(func):
         def funcv(v):
             def f():
                 return func(v)
+
             return f
+
         for v in vers:
             v = str(v)
             f = funcv(v)
             checks['%s%s' % (name, v.replace('.', ''))] = (f, desc % v)
         return func
+
     return decorator
 
+
 def checkfeatures(features):
     result = {
         'error': [],
@@ -94,13 +108,15 @@
 
     return result
 
+
 def require(features):
     """Require that features are available, exiting if not."""
     result = checkfeatures(features)
 
     for missing in result['missing']:
-        stderr.write(('skipped: unknown feature: %s\n'
-                      % missing).encode('utf-8'))
+        stderr.write(
+            ('skipped: unknown feature: %s\n' % missing).encode('utf-8')
+        )
     for msg in result['skipped']:
         stderr.write(('skipped: %s\n' % msg).encode('utf-8'))
     for msg in result['error']:
@@ -112,21 +128,25 @@
     if result['skipped'] or result['error']:
         sys.exit(1)
 
+
 def matchoutput(cmd, regexp, ignorestatus=False):
     """Return the match object if cmd executes successfully and its output
     is matched by the supplied regular expression.
     """
     r = re.compile(regexp)
     p = subprocess.Popen(
-        cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
     s = p.communicate()[0]
     ret = p.returncode
     return (ignorestatus or not ret) and r.search(s)
 
+
 @check("baz", "GNU Arch baz client")
 def has_baz():
     return matchoutput('baz --version 2>&1', br'baz Bazaar version')
 
+
 @check("bzr", "Canonical's Bazaar client")
 def has_bzr():
     try:
@@ -135,48 +155,61 @@
         import bzrlib.errors
         import bzrlib.revision
         import bzrlib.revisionspec
+
         bzrlib.revisionspec.RevisionSpec
         return bzrlib.__doc__ is not None
     except (AttributeError, ImportError):
         return False
 
+
 @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,))
 def has_bzr_range(v):
     major, minor = v.split('rc')[0].split('.')[0:2]
     try:
         import bzrlib
-        return (bzrlib.__doc__ is not None
-                and bzrlib.version_info[:2] >= (int(major), int(minor)))
+
+        return bzrlib.__doc__ is not None and bzrlib.version_info[:2] >= (
+            int(major),
+            int(minor),
+        )
     except ImportError:
         return False
 
+
 @check("chg", "running with chg")
 def has_chg():
     return 'CHGHG' in os.environ
 
+
 @check("cvs", "cvs client/server")
 def has_cvs():
     re = br'Concurrent Versions System.*?server'
     return matchoutput('cvs --version 2>&1', re) and not has_msys()
 
+
 @check("cvs112", "cvs client/server 1.12.* (not cvsnt)")
 def has_cvs112():
     re = br'Concurrent Versions System \(CVS\) 1.12.*?server'
     return matchoutput('cvs --version 2>&1', re) and not has_msys()
 
+
 @check("cvsnt", "cvsnt client/server")
 def has_cvsnt():
     re = br'Concurrent Versions System \(CVSNT\) (\d+).(\d+).*\(client/server\)'
     return matchoutput('cvsnt --version 2>&1', re)
 
+
 @check("darcs", "darcs client")
 def has_darcs():
     return matchoutput('darcs --version', br'\b2\.([2-9]|\d{2})', True)
 
+
 @check("mtn", "monotone client (>= 1.0)")
 def has_mtn():
     return matchoutput('mtn --version', br'monotone', True) and not matchoutput(
-        'mtn --version', br'monotone 0\.', True)
+        'mtn --version', br'monotone 0\.', True
+    )
+
 
 @check("eol-in-paths", "end-of-lines in paths")
 def has_eol_in_paths():
@@ -188,6 +221,7 @@
     except (IOError, OSError):
         return False
 
+
 @check("execbit", "executable bit")
 def has_executablebit():
     try:
@@ -198,7 +232,7 @@
             m = os.stat(fn).st_mode & 0o777
             new_file_has_exec = m & EXECFLAGS
             os.chmod(fn, m ^ EXECFLAGS)
-            exec_flags_cannot_flip = ((os.stat(fn).st_mode & 0o777) == m)
+            exec_flags_cannot_flip = (os.stat(fn).st_mode & 0o777) == m
         finally:
             os.unlink(fn)
     except (IOError, OSError):
@@ -206,6 +240,7 @@
         return False
     return not (new_file_has_exec or exec_flags_cannot_flip)
 
+
 @check("icasefs", "case insensitive file system")
 def has_icasefs():
     # Stolen from mercurial.util
@@ -225,6 +260,7 @@
     finally:
         os.remove(path)
 
+
 @check("fifo", "named pipes")
 def has_fifo():
     if getattr(os, "mkfifo", None) is None:
@@ -237,10 +273,12 @@
     except OSError:
         return False
 
+
 @check("killdaemons", 'killdaemons.py support')
 def has_killdaemons():
     return True
 
+
 @check("cacheable", "cacheable filesystem")
 def has_cacheable_fs():
     from mercurial import util
@@ -252,59 +290,71 @@
     finally:
         os.remove(path)
 
+
 @check("lsprof", "python lsprof module")
 def has_lsprof():
     try:
         import _lsprof
-        _lsprof.Profiler # silence unused import warning
+
+        _lsprof.Profiler  # silence unused import warning
         return True
     except ImportError:
         return False
 
+
 def gethgversion():
     m = matchoutput('hg --version --quiet 2>&1', br'(\d+)\.(\d+)')
     if not m:
         return (0, 0)
     return (int(m.group(1)), int(m.group(2)))
 
-@checkvers("hg", "Mercurial >= %s",
-            list([(1.0 * x) / 10 for x in range(9, 99)]))
+
+@checkvers(
+    "hg", "Mercurial >= %s", list([(1.0 * x) / 10 for x in range(9, 99)])
+)
 def has_hg_range(v):
     major, minor = v.split('.')[0:2]
     return gethgversion() >= (int(major), int(minor))
 
+
 @check("hg08", "Mercurial >= 0.8")
 def has_hg08():
     if checks["hg09"][0]():
         return True
     return matchoutput('hg help annotate 2>&1', '--date')
 
+
 @check("hg07", "Mercurial >= 0.7")
 def has_hg07():
     if checks["hg08"][0]():
         return True
     return matchoutput('hg --version --quiet 2>&1', 'Mercurial Distributed SCM')
 
+
 @check("hg06", "Mercurial >= 0.6")
 def has_hg06():
     if checks["hg07"][0]():
         return True
     return matchoutput('hg --version --quiet 2>&1', 'Mercurial version')
 
+
 @check("gettext", "GNU Gettext (msgfmt)")
 def has_gettext():
     return matchoutput('msgfmt --version', br'GNU gettext-tools')
 
+
 @check("git", "git command line client")
 def has_git():
     return matchoutput('git --version 2>&1', br'^git version')
 
+
 def getgitversion():
     m = matchoutput('git --version 2>&1', br'git version (\d+)\.(\d+)')
     if not m:
         return (0, 0)
     return (int(m.group(1)), int(m.group(2)))
 
+
 # https://github.com/git-lfs/lfs-test-server
 @check("lfs-test-server", "git-lfs test server")
 def has_lfsserver():
@@ -316,40 +366,49 @@
         for path in os.environ["PATH"].split(os.pathsep)
     )
 
+
 @checkvers("git", "git client (with ext::sh support) version >= %s", (1.9,))
 def has_git_range(v):
     major, minor = v.split('.')[0:2]
     return getgitversion() >= (int(major), int(minor))
 
+
 @check("docutils", "Docutils text processing library")
 def has_docutils():
     try:
         import docutils.core
-        docutils.core.publish_cmdline # silence unused import
+
+        docutils.core.publish_cmdline  # silence unused import
         return True
     except ImportError:
         return False
 
+
 def getsvnversion():
     m = matchoutput('svn --version --quiet 2>&1', br'^(\d+)\.(\d+)')
     if not m:
         return (0, 0)
     return (int(m.group(1)), int(m.group(2)))
 
+
 @checkvers("svn", "subversion client and admin tools >= %s", (1.3, 1.5))
 def has_svn_range(v):
     major, minor = v.split('.')[0:2]
     return getsvnversion() >= (int(major), int(minor))
 
+
 @check("svn", "subversion client and admin tools")
 def has_svn():
-    return (matchoutput('svn --version 2>&1', br'^svn, version') and
-            matchoutput('svnadmin --version 2>&1', br'^svnadmin, version'))
+    return matchoutput('svn --version 2>&1', br'^svn, version') and matchoutput(
+        'svnadmin --version 2>&1', br'^svnadmin, version'
+    )
+
 
 @check("svn-bindings", "subversion python bindings")
 def has_svn_bindings():
     try:
         import svn.core
+
         version = svn.core.SVN_VER_MAJOR, svn.core.SVN_VER_MINOR
         if version < (1, 4):
             return False
@@ -357,10 +416,13 @@
     except ImportError:
         return False
 
+
 @check("p4", "Perforce server and client")
 def has_p4():
-    return (matchoutput('p4 -V', br'Rev\. P4/') and
-            matchoutput('p4d -V', br'Rev\. P4D/'))
+    return matchoutput('p4 -V', br'Rev\. P4/') and matchoutput(
+        'p4d -V', br'Rev\. P4D/'
+    )
+
 
 @check("symlink", "symbolic links")
 def has_symlink():
@@ -374,9 +436,11 @@
     except (OSError, AttributeError):
         return False
 
+
 @check("hardlink", "hardlinks")
 def has_hardlink():
     from mercurial import util
+
     fh, fn = tempfile.mkstemp(dir='.', prefix=tempprefix)
     os.close(fh)
     name = tempfile.mktemp(dir='.', prefix=tempprefix)
@@ -389,15 +453,18 @@
     finally:
         os.unlink(fn)
 
+
 @check("hardlink-whitelisted", "hardlinks on whitelisted filesystems")
 def has_hardlink_whitelisted():
     from mercurial import util
+
     try:
         fstype = util.getfstype(b'.')
     except OSError:
         return False
     return fstype in util._hardlinkfswhitelist
 
+
 @check("rmcwd", "can remove current working directory")
 def has_rmcwd():
     ocwd = os.getcwd()
@@ -418,22 +485,27 @@
         except OSError:
             pass
 
+
 @check("tla", "GNU Arch tla client")
 def has_tla():
     return matchoutput('tla --version 2>&1', br'The GNU Arch Revision')
 
+
 @check("gpg", "gpg client")
 def has_gpg():
     return matchoutput('gpg --version 2>&1', br'GnuPG')
 
+
 @check("gpg2", "gpg client v2")
 def has_gpg2():
     return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.')
 
+
 @check("gpg21", "gpg client v2.1+")
 def has_gpg21():
     return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)')
 
+
 @check("unix-permissions", "unix-style permissions")
 def has_unix_permissions():
     d = tempfile.mkdtemp(dir='.', prefix=tempprefix)
@@ -451,25 +523,30 @@
     finally:
         os.rmdir(d)
 
+
 @check("unix-socket", "AF_UNIX socket family")
 def has_unix_socket():
     return getattr(socket, 'AF_UNIX', None) is not None
 
+
 @check("root", "root permissions")
 def has_root():
     return getattr(os, 'geteuid', None) and os.geteuid() == 0
 
+
 @check("pyflakes", "Pyflakes python linter")
 def has_pyflakes():
-    return matchoutput("sh -c \"echo 'import re' 2>&1 | pyflakes\"",
-                       br"<stdin>:1: 're' imported but unused",
-                       True)
+    return matchoutput(
+        "sh -c \"echo 'import re' 2>&1 | pyflakes\"",
+        br"<stdin>:1: 're' imported but unused",
+        True,
+    )
+
 
 @check("pylint", "Pylint python linter")
 def has_pylint():
-    return matchoutput("pylint --help",
-                       br"Usage:  pylint",
-                       True)
+    return matchoutput("pylint --help", br"Usage:  pylint", True)
+
 
 @check("clang-format", "clang-format C code formatter")
 def has_clang_format():
@@ -477,49 +554,59 @@
     # style changed somewhere between 4.x and 6.x
     return m and int(m.group(1)) >= 6
 
+
 @check("jshint", "JSHint static code analysis tool")
 def has_jshint():
     return matchoutput("jshint --version 2>&1", br"jshint v")
 
+
 @check("pygments", "Pygments source highlighting library")
 def has_pygments():
     try:
         import pygments
-        pygments.highlight # silence unused import warning
+
+        pygments.highlight  # silence unused import warning
         return True
     except ImportError:
         return False
 
+
 @check("outer-repo", "outer repo")
 def has_outer_repo():
     # failing for other reasons than 'no repo' imply that there is a repo
-    return not matchoutput('hg root 2>&1',
-                           br'abort: no repository found', True)
+    return not matchoutput('hg root 2>&1', br'abort: no repository found', True)
+
 
 @check("ssl", "ssl module available")
 def has_ssl():
     try:
         import ssl
+
         ssl.CERT_NONE
         return True
     except ImportError:
         return False
 
+
 @check("sslcontext", "python >= 2.7.9 ssl")
 def has_sslcontext():
     try:
         import ssl
+
         ssl.SSLContext
         return True
     except (ImportError, AttributeError):
         return False
 
+
 @check("defaultcacerts", "can verify SSL certs by system's CA certs store")
 def has_defaultcacerts():
     from mercurial import sslutil, ui as uimod
+
     ui = uimod.ui.load()
     return sslutil._defaultcacerts(ui) or sslutil._canloaddefaultcerts
 
+
 @check("defaultcacertsloaded", "detected presence of loaded system CA certs")
 def has_defaultcacertsloaded():
     import ssl
@@ -540,67 +627,82 @@
 
     return len(ctx.get_ca_certs()) > 0
 
+
 @check("tls1.2", "TLS 1.2 protocol support")
 def has_tls1_2():
     from mercurial import sslutil
+
     return b'tls1.2' in sslutil.supportedprotocols
 
+
 @check("windows", "Windows")
 def has_windows():
     return os.name == 'nt'
 
+
 @check("system-sh", "system() uses sh")
 def has_system_sh():
     return os.name != 'nt'
 
+
 @check("serve", "platform and python can manage 'hg serve -d'")
 def has_serve():
     return True
 
+
 @check("test-repo", "running tests from repository")
 def has_test_repo():
     t = os.environ["TESTDIR"]
     return os.path.isdir(os.path.join(t, "..", ".hg"))
 
+
 @check("tic", "terminfo compiler and curses module")
 def has_tic():
     try:
         import curses
+
         curses.COLOR_BLUE
         return matchoutput('test -x "`which tic`"', br'')
     except ImportError:
         return False
 
+
 @check("msys", "Windows with MSYS")
 def has_msys():
     return os.getenv('MSYSTEM')
 
+
 @check("aix", "AIX")
 def has_aix():
     return sys.platform.startswith("aix")
 
+
 @check("osx", "OS X")
 def has_osx():
     return sys.platform == 'darwin'
 
+
 @check("osxpackaging", "OS X packaging tools")
 def has_osxpackaging():
     try:
-        return (matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
-                and matchoutput(
-                    'productbuild', br'Usage: productbuild ',
-                    ignorestatus=1)
-                and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
-                and matchoutput(
-                    'xar --help', br'Usage: xar', ignorestatus=1))
+        return (
+            matchoutput('pkgbuild', br'Usage: pkgbuild ', ignorestatus=1)
+            and matchoutput(
+                'productbuild', br'Usage: productbuild ', ignorestatus=1
+            )
+            and matchoutput('lsbom', br'Usage: lsbom', ignorestatus=1)
+            and matchoutput('xar --help', br'Usage: xar', ignorestatus=1)
+        )
     except ImportError:
         return False
 
+
 @check('linuxormacos', 'Linux or MacOS')
 def has_linuxormacos():
     # This isn't a perfect test for MacOS. But it is sufficient for our needs.
     return sys.platform.startswith(('linux', 'darwin'))
 
+
 @check("docker", "docker support")
 def has_docker():
     pat = br'A self-sufficient runtime for'
@@ -618,33 +720,42 @@
         return True
     return False
 
+
 @check("debhelper", "debian packaging tools")
 def has_debhelper():
     # Some versions of dpkg say `dpkg', some say 'dpkg' (` vs ' on the first
     # quote), so just accept anything in that spot.
-    dpkg = matchoutput('dpkg --version',
-                       br"Debian .dpkg' package management program")
-    dh = matchoutput('dh --help',
-                     br'dh is a part of debhelper.', ignorestatus=True)
-    dh_py2 = matchoutput('dh_python2 --help',
-                         br'other supported Python versions')
+    dpkg = matchoutput(
+        'dpkg --version', br"Debian .dpkg' package management program"
+    )
+    dh = matchoutput(
+        'dh --help', br'dh is a part of debhelper.', ignorestatus=True
+    )
+    dh_py2 = matchoutput(
+        'dh_python2 --help', br'other supported Python versions'
+    )
     # debuild comes from the 'devscripts' package, though you might want
     # the 'build-debs' package instead, which has a dependency on devscripts.
-    debuild = matchoutput('debuild --help',
-                          br'to run debian/rules with given parameter')
+    debuild = matchoutput(
+        'debuild --help', br'to run debian/rules with given parameter'
+    )
     return dpkg and dh and dh_py2 and debuild
 
-@check("debdeps",
-       "debian build dependencies (run dpkg-checkbuilddeps in contrib/)")
+
+@check(
+    "debdeps", "debian build dependencies (run dpkg-checkbuilddeps in contrib/)"
+)
 def has_debdeps():
     # just check exit status (ignoring output)
     path = '%s/../contrib/packaging/debian/control' % os.environ['TESTDIR']
     return matchoutput('dpkg-checkbuilddeps %s' % path, br'')
 
+
 @check("demandimport", "demandimport enabled")
 def has_demandimport():
     # chg disables demandimport intentionally for performance wins.
-    return ((not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable')
+    return (not has_chg()) and os.environ.get('HGDEMANDIMPORT') != 'disable'
+
 
 @checkvers("py", "Python >= %s", (2.7, 3.5, 3.6, 3.7, 3.8, 3.9))
 def has_python_range(v):
@@ -653,73 +764,91 @@
 
     return (py_major, py_minor) >= (int(major), int(minor))
 
+
 @check("py3", "running with Python 3.x")
 def has_py3():
     return 3 == sys.version_info[0]
 
+
 @check("py3exe", "a Python 3.x interpreter is available")
 def has_python3exe():
     return matchoutput('python3 -V', br'^Python 3.(5|6|7|8|9)')
 
+
 @check("pure", "running with pure Python code")
 def has_pure():
-    return any([
-        os.environ.get("HGMODULEPOLICY") == "py",
-        os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
-    ])
+    return any(
+        [
+            os.environ.get("HGMODULEPOLICY") == "py",
+            os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure",
+        ]
+    )
+
 
 @check("slow", "allow slow tests (use --allow-slow-tests)")
 def has_slow():
     return os.environ.get('HGTEST_SLOW') == 'slow'
 
+
 @check("hypothesis", "Hypothesis automated test generation")
 def has_hypothesis():
     try:
         import hypothesis
+
         hypothesis.given
         return True
     except ImportError:
         return False
 
+
 @check("unziplinks", "unzip(1) understands and extracts symlinks")
 def unzip_understands_symlinks():
     return matchoutput('unzip --help', br'Info-ZIP')
 
+
 @check("zstd", "zstd Python module available")
 def has_zstd():
     try:
         import mercurial.zstd
+
         mercurial.zstd.__version__
         return True
     except ImportError:
         return False
 
+
 @check("devfull", "/dev/full special file")
 def has_dev_full():
     return os.path.exists('/dev/full')
 
+
 @check("virtualenv", "Python virtualenv support")
 def has_virtualenv():
     try:
         import virtualenv
+
         virtualenv.ACTIVATE_SH
         return True
     except ImportError:
         return False
 
+
 @check("fsmonitor", "running tests with fsmonitor")
 def has_fsmonitor():
     return 'HGFSMONITOR_TESTS' in os.environ
 
+
 @check("fuzzywuzzy", "Fuzzy string matching library")
 def has_fuzzywuzzy():
     try:
         import fuzzywuzzy
+
         fuzzywuzzy.__version__
         return True
     except ImportError:
         return False
 
+
 @check("clang-libfuzzer", "clang new enough to include libfuzzer")
 def has_clang_libfuzzer():
     mat = matchoutput('clang --version', br'clang version (\d)')
@@ -728,23 +857,28 @@
         return int(mat.group(1)) > 5
     return False
 
+
 @check("clang-6.0", "clang 6.0 with version suffix (libfuzzer included)")
 def has_clang60():
     return matchoutput('clang-6.0 --version', br'clang version 6\.')
 
+
 @check("xdiff", "xdiff algorithm")
 def has_xdiff():
     try:
         from mercurial import policy
+
         bdiff = policy.importmod('bdiff')
         return bdiff.xdiffblocks(b'', b'') == [(0, 0, 0, 0)]
     except (ImportError, AttributeError):
         return False
 
+
 @check('extraextensions', 'whether tests are running with extra extensions')
 def has_extraextensions():
     return 'HGTESTEXTRAEXTENSIONS' in os.environ
 
+
 def getrepofeatures():
     """Obtain set of repository features in use.
 
@@ -783,26 +917,32 @@
 
     return features
 
+
 @check('reporevlogstore', 'repository using the default revlog store')
 def has_reporevlogstore():
     return 'revlogstore' in getrepofeatures()
 
+
 @check('reposimplestore', 'repository using simple storage extension')
 def has_reposimplestore():
     return 'simplestore' in getrepofeatures()
 
+
 @check('repobundlerepo', 'whether we can open bundle files as repos')
 def has_repobundlerepo():
     return 'bundlerepo' in getrepofeatures()
 
+
 @check('repofncache', 'repository has an fncache')
 def has_repofncache():
     return 'fncache' in getrepofeatures()
 
+
 @check('sqlite', 'sqlite3 module is available')
 def has_sqlite():
     try:
         import sqlite3
+
         version = sqlite3.sqlite_version_info
     except ImportError:
         return False
@@ -813,16 +953,19 @@
 
     return matchoutput('sqlite3 -version', br'^3\.\d+')
 
+
 @check('vcr', 'vcr http mocking library')
 def has_vcr():
     try:
         import vcr
+
         vcr.VCR
         return True
     except (ImportError, AttributeError):
         pass
     return False
 
+
 @check('emacs', 'GNU Emacs')
 def has_emacs():
     # Our emacs lisp uses `with-eval-after-load` which is new in emacs