setup.py
branchstable
changeset 49366 288de6f5d724
parent 49310 050dc8730858
child 49394 5cf73de964e1
--- a/setup.py	Thu Jun 16 15:15:03 2022 +0200
+++ b/setup.py	Thu Jun 16 15:28:54 2022 +0200
@@ -5,99 +5,24 @@
 # 'python setup.py --help' for more options
 import os
 
-# Mercurial will never work on Python 3 before 3.5 due to a lack
-# of % formatting on bytestrings, and can't work on 3.6.0 or 3.6.1
-# due to a bug in % formatting in bytestrings.
-# We cannot support Python 3.5.0, 3.5.1, 3.5.2 because of bug in
-# codecs.escape_encode() where it raises SystemError on empty bytestring
-# bug link: https://bugs.python.org/issue25270
+# Mercurial can't work on 3.6.0 or 3.6.1 due to a bug in % formatting
+# in bytestrings.
 supportedpy = ','.join(
     [
-        '>=2.7.4',
-        '!=3.0.*',
-        '!=3.1.*',
-        '!=3.2.*',
-        '!=3.3.*',
-        '!=3.4.*',
-        '!=3.5.0',
-        '!=3.5.1',
-        '!=3.5.2',
-        '!=3.6.0',
-        '!=3.6.1',
+        '>=3.6.2',
     ]
 )
 
 import sys, platform
 import sysconfig
 
-if sys.version_info[0] >= 3:
-    printf = eval('print')
-    libdir_escape = 'unicode_escape'
 
-    def sysstr(s):
-        return s.decode('latin-1')
-
-
-else:
-    libdir_escape = 'string_escape'
-
-    def printf(*args, **kwargs):
-        f = kwargs.get('file', sys.stdout)
-        end = kwargs.get('end', '\n')
-        f.write(b' '.join(args) + end)
-
-    def sysstr(s):
-        return s
+def sysstr(s):
+    return s.decode('latin-1')
 
 
-# Attempt to guide users to a modern pip - this means that 2.6 users
-# should have a chance of getting a 4.2 release, and when we ratchet
-# the version requirement forward again hopefully everyone will get
-# something that works for them.
-if sys.version_info < (2, 7, 4, 'final'):
-    pip_message = (
-        'This may be due to an out of date pip. '
-        'Make sure you have pip >= 9.0.1.'
-    )
-    try:
-        import pip
-
-        pip_version = tuple([int(x) for x in pip.__version__.split('.')[:3]])
-        if pip_version < (9, 0, 1):
-            pip_message = (
-                'Your pip version is out of date, please install '
-                'pip >= 9.0.1. pip {} detected.'.format(pip.__version__)
-            )
-        else:
-            # pip is new enough - it must be something else
-            pip_message = ''
-    except Exception:
-        pass
-    error = """
-Mercurial does not support Python older than 2.7.4.
-Python {py} detected.
-{pip}
-""".format(
-        py=sys.version_info, pip=pip_message
-    )
-    printf(error, file=sys.stderr)
-    sys.exit(1)
-
 import ssl
 
-try:
-    ssl.SSLContext
-except AttributeError:
-    error = """
-The `ssl` module does not have the `SSLContext` class. This indicates an old
-Python version which does not support modern security features (which were
-added to Python 2.7 as part of "PEP 466"). Please make sure you have installed
-at least Python 2.7.9 or a Python version with backports of these security
-features.
-"""
-    printf(error, file=sys.stderr)
-    sys.exit(1)
-
 # ssl.HAS_TLSv1* are preferred to check support but they were added in Python
 # 3.7. Prior to CPython commit 6e8cda91d92da72800d891b2fc2073ecbc134d98
 # (backported to the 3.7 branch), ssl.PROTOCOL_TLSv1_1 / ssl.PROTOCOL_TLSv1_2
@@ -117,14 +42,10 @@
 version enabling these features (likely this requires the OpenSSL version to
 be at least 1.0.1).
 """
-    printf(error, file=sys.stderr)
+    print(error, file=sys.stderr)
     sys.exit(1)
 
-if sys.version_info[0] >= 3:
-    DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
-else:
-    # deprecated in Python 3
-    DYLIB_SUFFIX = sysconfig.get_config_vars()['SO']
+DYLIB_SUFFIX = sysconfig.get_config_vars()['EXT_SUFFIX']
 
 # Solaris Python packaging brain damage
 try:
@@ -174,7 +95,6 @@
 ispypy = "PyPy" in sys.version
 
 import ctypes
-import errno
 import stat, subprocess, time
 import re
 import shutil
@@ -276,7 +196,7 @@
 try:
     import py2exe
 
-    py2exe.Distribution  # silence unused import warning
+    py2exe.patch_distutils()
     py2exeloaded = True
     # import py2exe's patched Distribution class
     from distutils.core import Distribution
@@ -292,7 +212,7 @@
     return p.returncode, out, err
 
 
-class hgcommand(object):
+class hgcommand:
     def __init__(self, cmd, env):
         self.cmd = cmd
         self.env = env
@@ -302,8 +222,8 @@
         returncode, out, err = runcmd(cmd, self.env)
         err = filterhgerr(err)
         if err or returncode != 0:
-            printf("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
-            printf(err, file=sys.stderr)
+            print("stderr from '%s':" % (' '.join(cmd)), file=sys.stderr)
+            print(err, file=sys.stderr)
             return b''
         return out
 
@@ -536,7 +456,7 @@
             if hgrustext != 'cpython' and hgrustext is not None:
                 if hgrustext:
                     msg = 'unknown HGWITHRUSTEXT value: %s' % hgrustext
-                    printf(msg, file=sys.stderr)
+                    print(msg, file=sys.stderr)
                 hgrustext = None
             self.rust = hgrustext is not None
             self.no_rust = not self.rust
@@ -810,12 +730,9 @@
 
                 # Copy the pythonXY.dll next to the binary so that it runs
                 # without tampering with PATH.
-                fsdecode = lambda x: x
-                if sys.version_info[0] >= 3:
-                    fsdecode = os.fsdecode
                 dest = os.path.join(
                     os.path.dirname(self.hgtarget),
-                    fsdecode(dllbasename),
+                    os.fsdecode(dllbasename),
                 )
 
                 if not os.path.exists(dest):
@@ -823,19 +740,18 @@
 
                 # Also overwrite python3.dll so that hgext.git is usable.
                 # TODO: also handle the MSYS flavor
-                if sys.version_info[0] >= 3:
-                    python_x = os.path.join(
-                        os.path.dirname(fsdecode(buf.value)),
-                        "python3.dll",
+                python_x = os.path.join(
+                    os.path.dirname(os.fsdecode(buf.value)),
+                    "python3.dll",
+                )
+
+                if os.path.exists(python_x):
+                    dest = os.path.join(
+                        os.path.dirname(self.hgtarget),
+                        os.path.basename(python_x),
                     )
 
-                    if os.path.exists(python_x):
-                        dest = os.path.join(
-                            os.path.dirname(self.hgtarget),
-                            os.path.basename(python_x),
-                        )
-
-                        shutil.copy(python_x, dest)
+                    shutil.copy(python_x, dest)
 
         if not pythonlib:
             log.warn(
@@ -850,14 +766,10 @@
             f.write(b'/* this file is autogenerated by setup.py */\n')
             f.write(b'#define HGPYTHONLIB "%s"\n' % pythonlib)
 
-        macros = None
-        if sys.version_info[0] >= 3:
-            macros = [('_UNICODE', None), ('UNICODE', None)]
-
         objects = self.compiler.compile(
             ['mercurial/exewrapper.c'],
             output_dir=self.build_temp,
-            macros=macros,
+            macros=[('_UNICODE', None), ('UNICODE', None)],
         )
         self.compiler.link_executable(
             objects, self.hgtarget, libraries=[], output_dir=self.build_temp
@@ -1069,6 +981,10 @@
         ),
     ]
 
+    sub_commands = install.sub_commands + [
+        ('install_completion', lambda self: True)
+    ]
+
     # Also helps setuptools not be sad while we refuse to create eggs.
     single_version_externally_managed = True
 
@@ -1183,11 +1099,43 @@
                 )
                 continue
 
-            data = data.replace(b'@LIBDIR@', libdir.encode(libdir_escape))
+            data = data.replace(b'@LIBDIR@', libdir.encode('unicode_escape'))
             with open(outfile, 'wb') as fp:
                 fp.write(data)
 
 
+class hginstallcompletion(Command):
+    description = 'Install shell completion'
+
+    def initialize_options(self):
+        self.install_dir = None
+        self.outputs = []
+
+    def finalize_options(self):
+        self.set_undefined_options(
+            'install_data', ('install_dir', 'install_dir')
+        )
+
+    def get_outputs(self):
+        return self.outputs
+
+    def run(self):
+        for src, dir_path, dest in (
+            (
+                'bash_completion',
+                ('share', 'bash-completion', 'completions'),
+                'hg',
+            ),
+            ('zsh_completion', ('share', 'zsh', 'site-functions'), '_hg'),
+        ):
+            dir = os.path.join(self.install_dir, *dir_path)
+            self.mkpath(dir)
+
+            dest = os.path.join(dir, dest)
+            self.outputs.append(dest)
+            self.copy_file(os.path.join('contrib', src), dest)
+
+
 # virtualenv installs custom distutils/__init__.py and
 # distutils/distutils.cfg files which essentially proxy back to the
 # "real" distutils in the main Python install. The presence of this
@@ -1278,6 +1226,7 @@
     'build_scripts': hgbuildscripts,
     'build_hgextindex': buildhgextindex,
     'install': hginstall,
+    'install_completion': hginstallcompletion,
     'install_lib': hginstalllib,
     'install_scripts': hginstallscripts,
     'build_hgexe': buildhgexe,
@@ -1324,27 +1273,12 @@
     'hgdemandimport',
 ]
 
-# The pygit2 dependency dropped py2 support with the 1.0 release in Dec 2019.
-# Prior releases do not build at all on Windows, because Visual Studio 2008
-# doesn't understand C 11.  Older Linux releases are buggy.
-if sys.version_info[0] == 2:
-    packages.remove('hgext.git')
-
-
 for name in os.listdir(os.path.join('mercurial', 'templates')):
     if name != '__pycache__' and os.path.isdir(
         os.path.join('mercurial', 'templates', name)
     ):
         packages.append('mercurial.templates.%s' % name)
 
-if sys.version_info[0] == 2:
-    packages.extend(
-        [
-            'mercurial.thirdparty.concurrent',
-            'mercurial.thirdparty.concurrent.futures',
-        ]
-    )
-
 if 'HG_PY2EXE_EXTRA_INSTALL_PACKAGES' in os.environ:
     # py2exe can't cope with namespace packages very well, so we have to
     # install any hgext3rd.* extensions that we want in the final py2exe
@@ -1476,19 +1410,9 @@
 
         cargocmd = ['cargo', 'rustc', '--release']
 
-        feature_flags = []
-
-        cargocmd.append('--no-default-features')
-        if sys.version_info[0] == 2:
-            feature_flags.append('python27')
-        elif sys.version_info[0] == 3:
-            feature_flags.append('python3')
-
         rust_features = env.get("HG_RUST_FEATURES")
         if rust_features:
-            feature_flags.append(rust_features)
-
-        cargocmd.extend(('--features', " ".join(feature_flags)))
+            cargocmd.extend(('--features', rust_features))
 
         cargocmd.append('--')
         if sys.platform == 'darwin':
@@ -1497,15 +1421,12 @@
             )
         try:
             subprocess.check_call(cargocmd, env=env, cwd=self.rustsrcdir)
-        except OSError as exc:
-            if exc.errno == errno.ENOENT:
-                raise RustCompilationError("Cargo not found")
-            elif exc.errno == errno.EACCES:
-                raise RustCompilationError(
-                    "Cargo found, but permission to execute it is denied"
-                )
-            else:
-                raise
+        except FileNotFoundError:
+            raise RustCompilationError("Cargo not found")
+        except PermissionError:
+            raise RustCompilationError(
+                "Cargo found, but permission to execute it is denied"
+            )
         except subprocess.CalledProcessError:
             raise RustCompilationError(
                 "Cargo failed. Working directory: %r, "
@@ -1640,7 +1561,7 @@
     # the cygwinccompiler package is not available on some Python
     # distributions like the ones from the optware project for Synology
     # DiskStation boxes
-    class HackedMingw32CCompiler(object):
+    class HackedMingw32CCompiler:
         pass
 
 
@@ -1763,9 +1684,7 @@
 if sys.platform == 'darwin' and os.path.exists('/usr/bin/xcodebuild'):
     version = runcmd(['/usr/bin/xcodebuild', '-version'], {})[1].splitlines()
     if version:
-        version = version[0]
-        if sys.version_info[0] == 3:
-            version = version.decode('utf-8')
+        version = version[0].decode('utf-8')
         xcode4 = version.startswith('Xcode') and StrictVersion(
             version.split()[1]
         ) >= StrictVersion('4.0')