comparison tests/run-tests.py @ 52664:f5091286b10c

packaging: modernize (compat PEP 517) with less distutils and setup.py calls - setup.py: less distutils imports and setuptools required distutils is deprecated and one should import commands from setuptools to support modern workflows depending on PEP 517 and 518. Moreover, for Python >=3.12, distutils comes from setuptools. It corresponds to old and unmaintain code that do not support PEP 517. The PEP 517 frontends (pip, build, pipx, PDM, UV, etc.) are responsible for creating a venv just for the build. The build dependencies (currently only setuptools) are specified in the pyproject.toml file. Therefore, there is no reason to support building without setuptools. Calling directly setup.py is deprecated and we have to use a PEP 517 frontend. For this commit we use pip with venv. - run-tests.py: install with pip instead of direct call of setup.py Mercurial is then built in an isolated environment. - Makefile: use venv+pip instead of setup.py
author paugier <pierre.augier@univ-grenoble-alpes.fr>
date Wed, 08 Jan 2025 05:07:00 +0100
parents 70a75d379daf
children ba8aa20ff31f
comparison
equal deleted inserted replaced
52663:25bb409da058 52664:f5091286b10c
85 sys.exit(70) # EX_SOFTWARE from `man 3 sysexit` 85 sys.exit(70) # EX_SOFTWARE from `man 3 sysexit`
86 86
87 MACOS = sys.platform == 'darwin' 87 MACOS = sys.platform == 'darwin'
88 WINDOWS = os.name == r'nt' 88 WINDOWS = os.name == r'nt'
89 shellquote = shlex.quote 89 shellquote = shlex.quote
90 BINDIRNAME = b"Scripts" if WINDOWS else b"bin"
90 91
91 # The number of HGPORTx ports allocated to each test. 92 # The number of HGPORTx ports allocated to each test.
92 HGPORT_COUNT = 4 93 HGPORT_COUNT = 4
93 94
94 RUNTEST_DIR = os.path.abspath(os.path.dirname(__file__.encode('utf-8'))) 95 RUNTEST_DIR = os.path.abspath(os.path.dirname(__file__.encode('utf-8')))
750 ) 751 )
751 if options.pyoxidized: 752 if options.pyoxidized:
752 parser.error('--pyoxidized does not work with --local (yet)') 753 parser.error('--pyoxidized does not work with --local (yet)')
753 testdir = os.path.dirname(_sys2bytes(canonpath(sys.argv[0]))) 754 testdir = os.path.dirname(_sys2bytes(canonpath(sys.argv[0])))
754 reporootdir = os.path.dirname(testdir) 755 reporootdir = os.path.dirname(testdir)
755 pathandattrs = [(b'hg', 'with_hg')] 756 venv_local = b'.venv_%s%d.%d' % (
757 sys.implementation.name.encode(),
758 sys.version_info.major,
759 sys.version_info.minor,
760 )
761 path_local_hg = os.path.join(reporootdir, venv_local, BINDIRNAME, b"hg")
762 if not os.path.exists(path_local_hg):
763 # no local environment but we can still use ./hg to please test-run-tests.t
764 path_local_hg = os.path.join(reporootdir, b"hg")
765
766 pathandattrs = [(path_local_hg, 'with_hg')]
756 if options.chg: 767 if options.chg:
757 pathandattrs.append((b'contrib/chg/chg', 'with_chg')) 768 pathandattrs.append((b'contrib/chg/chg', 'with_chg'))
758 if options.rhg: 769 if options.rhg:
759 pathandattrs.append((b'rust/target/release/rhg', 'with_rhg')) 770 pathandattrs.append((b'rust/target/release/rhg', 'with_rhg'))
760 for relpath, attr in pathandattrs: 771 for relpath, attr in pathandattrs:
2803 os.rename(tmpname, timepath) 2814 os.rename(tmpname, timepath)
2804 except OSError: 2815 except OSError:
2805 pass 2816 pass
2806 2817
2807 2818
2819 def get_site_packages_dir(python_exe):
2820 return subprocess.run(
2821 [
2822 python_exe,
2823 "-c",
2824 "import sys; print([p for p in sys.path if p.startswith(sys.prefix) and p.endswith('site-packages')][0])",
2825 ],
2826 check=True,
2827 capture_output=True,
2828 ).stdout.strip()
2829
2830
2808 class TextTestRunner(unittest.TextTestRunner): 2831 class TextTestRunner(unittest.TextTestRunner):
2809 """Custom unittest test runner that uses appropriate settings.""" 2832 """Custom unittest test runner that uses appropriate settings."""
2810 2833
2811 def __init__(self, runner, *args, **kwargs): 2834 def __init__(self, runner, *args, **kwargs):
2812 super().__init__(*args, **kwargs) 2835 super().__init__(*args, **kwargs)
3258 # Other Python scripts in the test harness need to 3281 # Other Python scripts in the test harness need to
3259 # `import mercurial`. If `hg` is a Python script, we assume 3282 # `import mercurial`. If `hg` is a Python script, we assume
3260 # the Mercurial modules are relative to its path and tell the tests 3283 # the Mercurial modules are relative to its path and tell the tests
3261 # to load Python modules from its directory. 3284 # to load Python modules from its directory.
3262 with open(whg, 'rb') as fh: 3285 with open(whg, 'rb') as fh:
3263 initial = fh.read(1024) 3286 first_line = fh.readline()
3264 3287
3265 if re.match(b'#!.*python', initial): 3288 if re.match(b'#!.*python', first_line):
3266 self._pythondir = self._bindir 3289 python_exe = first_line.split(b"#!")[1].strip()
3290 try:
3291 self._pythondir = get_site_packages_dir(python_exe)
3292 except (FileNotFoundError, subprocess.CalledProcessError):
3293 self._pythondir = self._bindir
3294 elif self.options.local:
3295 assert WINDOWS
3296 python_exe = os.path.join(self._bindir, b"python.exe")
3297 self._pythondir = get_site_packages_dir(python_exe)
3267 # If it looks like our in-repo Rust binary, use the source root. 3298 # If it looks like our in-repo Rust binary, use the source root.
3268 # This is a bit hacky. But rhg is still not supported outside the 3299 # This is a bit hacky. But rhg is still not supported outside the
3269 # source directory. So until it is, do the simple thing. 3300 # source directory. So until it is, do the simple thing.
3270 elif re.search(b'/rust/target/[^/]+/hg', normbin): 3301 elif re.search(b'/rust/target/[^/]+/hg', normbin):
3271 self._pythondir = os.path.dirname(self._testdir) 3302 self._pythondir = os.path.dirname(self._testdir)
3289 subprocess.run(command_create_venv, check=True) 3320 subprocess.run(command_create_venv, check=True)
3290 3321
3291 bindir = b"Scripts" if WINDOWS else b"bin" 3322 bindir = b"Scripts" if WINDOWS else b"bin"
3292 self._bindir = os.path.join(self._installdir, bindir) 3323 self._bindir = os.path.join(self._installdir, bindir)
3293 self._python = _bytes2sys(os.path.join(self._bindir, b"python")) 3324 self._python = _bytes2sys(os.path.join(self._bindir, b"python"))
3294 3325 self._pythondir = get_site_packages_dir(self._python)
3295 self._pythondir = subprocess.run(
3296 [
3297 self._python,
3298 "-c",
3299 "import sys; print([p for p in sys.path if p.startswith(sys.prefix) and p.endswith('site-packages')][0])",
3300 ],
3301 check=True,
3302 capture_output=True,
3303 ).stdout.strip()
3304 3326
3305 # Force the use of hg.exe instead of relying on MSYS to recognize hg is 3327 # Force the use of hg.exe instead of relying on MSYS to recognize hg is
3306 # a python script and feed it to python.exe. Legacy stdio is force 3328 # a python script and feed it to python.exe. Legacy stdio is force
3307 # enabled by hg.exe, and this is a more realistic way to launch hg 3329 # enabled by hg.exe, and this is a more realistic way to launch hg
3308 # anyway. 3330 # anyway.
3852 3874
3853 script = _sys2bytes(os.path.realpath(sys.argv[0])) 3875 script = _sys2bytes(os.path.realpath(sys.argv[0]))
3854 hgroot = os.path.dirname(os.path.dirname(script)) 3876 hgroot = os.path.dirname(os.path.dirname(script))
3855 self._hgroot = hgroot 3877 self._hgroot = hgroot
3856 os.chdir(hgroot) 3878 os.chdir(hgroot)
3857 cmd = [ 3879 cmd = [self._pythonb, b"-m", b"pip", b"install", b"."]
3858 self._pythonb,
3859 b"setup.py",
3860 ]
3861 if setup_opts: 3880 if setup_opts:
3862 cmd.append(setup_opts) 3881 cmd.extend(
3863 cmd.extend( 3882 [b"--config-settings", b"--global-option=%s" % setup_opts]
3864 [ 3883 )
3865 b"clean",
3866 b"--all",
3867 b"build",
3868 ]
3869 )
3870 cmd.extend(
3871 [
3872 b"--build-base=%s" % os.path.join(self._hgtmp, b"build"),
3873 b"install",
3874 b"--force",
3875 ]
3876 )
3877
3878 return cmd 3884 return cmd
3879 3885
3880 def _installhg(self): 3886 def _installhg(self):
3881 """Install hg into the test environment. 3887 """Install hg into the test environment.
3882 3888