From 40187bdbeff5b303b96c68e7c9f1fe7af7138b0c Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Thu, 11 May 2023 18:57:43 -0700 Subject: [PATCH 1/3] Replace ScriptRunner with pytest-console-scripts fixtures. --- delocate/tests/scriptrunner.py | 167 ----------- delocate/tests/test_scripts.py | 494 +++++++++++++++++--------------- delocate/tests/test_wheelies.py | 24 +- pyproject.toml | 2 +- test-requirements.txt | 1 + 5 files changed, 273 insertions(+), 415 deletions(-) delete mode 100644 delocate/tests/scriptrunner.py diff --git a/delocate/tests/scriptrunner.py b/delocate/tests/scriptrunner.py deleted file mode 100644 index 92db11be..00000000 --- a/delocate/tests/scriptrunner.py +++ /dev/null @@ -1,167 +0,0 @@ -""" Module to help tests check script output - -Provides class to be instantiated in tests that check scripts. Usually works -something like this in a test module:: - - from .scriptrunner import ScriptRunner - runner = ScriptRunner() - -Then, in the tests, something like:: - - code, stdout, stderr = runner.run_command(['my-script', my_arg]) - assert_equal(code, 0) - assert_equal(stdout, b'This script ran OK') -""" -import os -import sys -from os.path import dirname, isdir, isfile -from os.path import join as pjoin -from os.path import pathsep, realpath -from subprocess import PIPE, Popen - - -def _get_package(): - """Workaround for missing ``__package__`` in Python 3.2""" - if "__package__" in globals() and __package__ is not None: - return __package__ - return __name__.split(".", 1)[0] - - -# Same as __package__ for Python 2.6, 2.7 and >= 3.3 -MY_PACKAGE = _get_package() - - -def local_script_dir(script_sdir): - """Get local script directory if running in development dir, else None""" - # Check for presence of scripts in development directory. ``realpath`` - # allows for the situation where the development directory has been linked - # into the path. - package_path = dirname(__import__(MY_PACKAGE).__file__) - above_us = realpath(pjoin(package_path, "..")) - devel_script_dir = pjoin(above_us, script_sdir) - if isfile(pjoin(above_us, "setup.py")) and isdir(devel_script_dir): - return devel_script_dir - return None - - -def local_module_dir(module_name): - """Get local module directory if running in development dir, else None""" - mod = __import__(module_name) - containing_path = dirname(dirname(realpath(mod.__file__))) - if containing_path == realpath(os.getcwd()): - return containing_path - return None - - -class ScriptRunner(object): - """Class to run scripts and return output - - Finds local scripts and local modules if running in the development - directory, otherwise finds system scripts and modules. - """ - - def __init__( - self, - script_sdir="scripts", - module_sdir=MY_PACKAGE, - debug_print_var=None, - output_processor=lambda x: x, - ): - """Init ScriptRunner instance - - Parameters - ---------- - script_sdir : str, optional - Name of subdirectory in top-level directory (directory containing - setup.py), to find scripts in development tree. Typically - 'scripts', but might be 'bin'. - module_sdir : str, optional - Name of subdirectory in top-level directory (directory containing - setup.py), to find main package directory. - debug_print_vsr : str, optional - Name of environment variable that indicates whether to do debug - printing or no. - output_processor : callable - Callable to run on the stdout, stderr outputs before returning - them. Use this to convert bytes to unicode, strip whitespace, etc. - """ - self.local_script_dir = local_script_dir(script_sdir) - self.local_module_dir = local_module_dir(module_sdir) - if debug_print_var is None: - debug_print_var = "{0}_DEBUG_PRINT".format(module_sdir.upper()) - self.debug_print = os.environ.get(debug_print_var, False) - self.output_processor = output_processor - - def run_command(self, cmd, check_code=True): - """Run command sequence `cmd` returning exit code, stdout, stderr - - Parameters - ---------- - cmd : str or sequence - string with command name or sequence of strings defining command - check_code : {True, False}, optional - If True, raise error for non-zero return code - - Returns - ------- - returncode : int - return code from execution of `cmd` - stdout : bytes (python 3) or str (python 2) - stdout from `cmd` - stderr : bytes (python 3) or str (python 2) - stderr from `cmd` - """ - if isinstance(cmd, str): - cmd = [cmd] - else: - cmd = list(cmd) - if self.local_script_dir is not None: - # Windows can't run script files without extensions natively - # so we need to run local scripts (no extensions) via the - # Python interpreter. On Unix, we might have the wrong incantation - # for the Python interpreter in the hash bang first line in the - # source file. So, either way, run the script through the - # Python interpreter - cmd = [sys.executable, pjoin(self.local_script_dir, cmd[0])] + cmd[ - 1: - ] - elif os.name == "nt": - # Need .bat file extension for windows - cmd[0] += ".bat" - if os.name == "nt": - # Quote any arguments with spaces. The quotes delimit the arguments - # on Windows, and the arguments might be files paths with spaces. - # On Unix the list elements are each separate arguments. - cmd = ['"{0}"'.format(c) if " " in c else c for c in cmd] - if self.debug_print: - print("Running command '%s'" % cmd) - env = os.environ - if self.local_module_dir is not None: - # module likely comes from the current working directory. - # We might need that directory on the path if we're running - # the scripts from a temporary directory - env = env.copy() - pypath = env.get("PYTHONPATH", None) - if pypath is None: - env["PYTHONPATH"] = self.local_module_dir - else: - env["PYTHONPATH"] = self.local_module_dir + pathsep + pypath - proc = Popen(cmd, stdout=PIPE, stderr=PIPE, env=env) - stdout, stderr = proc.communicate() - if proc.poll() is None: - proc.terminate() - if check_code and proc.returncode != 0: - raise RuntimeError( - """Command "{0}" failed with - stdout - ------ - {1} - stderr - ------ - {2} - """.format( - cmd, stdout, stderr - ) - ) - opp = self.output_processor - return proc.returncode, opp(stdout), opp(stderr) diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index ab2d66c3..0561f8aa 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -6,24 +6,24 @@ the top-level folder ``scripts``. Otherwise try and get the scripts from the path """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations import os import shutil import subprocess import sys -from os.path import abspath, basename, dirname, exists, isfile +from os.path import basename, exists from os.path import join as pjoin from os.path import realpath, splitext -from typing import Text +from pathlib import Path +from typing import Any, Text import pytest +from typing_extensions import Protocol from ..tmpdirs import InGivenDirectory, InTemporaryDirectory from ..tools import dir2zip, set_install_name, zip2dir from ..wheeltools import InWheel -from .pytest_tools import assert_equal, assert_false, assert_raises, assert_true -from .scriptrunner import ScriptRunner from .test_delocating import _copy_to, _make_bare_depends, _make_libtree from .test_fuse import assert_same_tree from .test_install_names import EXT_LIBS @@ -45,16 +45,16 @@ assert_winfo_similar, ) +DATA_PATH = (Path(__file__).parent / "data").resolve(strict=True) -def _proc_lines(in_str): - """Decode `in_string` to str, split lines, strip whitespace - Remove any empty lines. +def _proc_lines(in_str: str) -> list[str]: + """Return input split across lines, striping whitespace, without blanks. Parameters ---------- - in_str : bytes - Input bytes for splitting, stripping + in_str : str + Input for splitting, stripping Returns ------- @@ -62,20 +62,30 @@ def _proc_lines(in_str): List of line ``str`` where each line has been stripped of leading and trailing whitespace and empty lines have been removed. """ - lines = in_str.decode("latin1").splitlines() + lines = in_str.splitlines() return [line.strip() for line in lines if line.strip() != ""] -lines_runner = ScriptRunner(output_processor=_proc_lines) -run_command = lines_runner.run_command -bytes_runner = ScriptRunner() +class RunResult(Protocol): + """Result of running a script.""" + success: bool + returncode: int + stdout: str + stderr: str -DATA_PATH = abspath(pjoin(dirname(__file__), "data")) +class ScriptRunner(Protocol): + """Protocol for pytest-console-scripts ScriptRunner.""" -@pytest.mark.xfail(sys.platform != "darwin", reason="Needs macOS linkage.") -def test_listdeps(plat_wheel: PlatWheel) -> None: + def run(self, command: str, *arguments: str, **options: Any) -> RunResult: + ... + + +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_listdeps(plat_wheel: PlatWheel, script_runner: ScriptRunner) -> None: # smokey tests of list dependencies command local_libs = { "liba.dylib", @@ -85,75 +95,78 @@ def test_listdeps(plat_wheel: PlatWheel) -> None: } # single path, with libs with InGivenDirectory(DATA_PATH): - code, stdout, stderr = run_command(["delocate-listdeps", DATA_PATH]) - assert set(stdout) == local_libs - assert code == 0 + result = script_runner.run("delocate-listdeps", str(DATA_PATH)) + assert result.success + assert set(_proc_lines(result.stdout)) == local_libs # single path, no libs with InTemporaryDirectory(): zip2dir(PURE_WHEEL, "pure") - code, stdout, stderr = run_command(["delocate-listdeps", "pure"]) - assert set(stdout) == set() - assert code == 0 + result = script_runner.run("delocate-listdeps", "pure") + assert result.success + assert result.stdout.strip() == "" + # Multiple paths one with libs zip2dir(plat_wheel.whl, "plat") - code, stdout, stderr = run_command( - ["delocate-listdeps", "pure", "plat"] - ) - assert stdout == ["pure:", "plat:", plat_wheel.stray_lib] - assert code == 0 + result = script_runner.run("delocate-listdeps", "pure", "plat") + assert result.success + assert _proc_lines(result.stdout) == [ + "pure:", + "plat:", + plat_wheel.stray_lib, + ] + # With -d flag, get list of dependending modules - code, stdout, stderr = run_command( - ["delocate-listdeps", "-d", "pure", "plat"] - ) - assert stdout == [ + result = script_runner.run("delocate-listdeps", "-d", "pure", "plat") + assert result.success + assert _proc_lines(result.stdout) == [ "pure:", "plat:", plat_wheel.stray_lib + ":", - pjoin("plat", "fakepkg1", "subpkg", "module2.abi3.so"), + str(Path("plat", "fakepkg1", "subpkg", "module2.abi3.so")), ] - assert code == 0 # With --all flag, get all dependencies with InGivenDirectory(DATA_PATH): - code, stdout, stderr = run_command( - ["delocate-listdeps", "--all", DATA_PATH] - ) + result = script_runner.run("delocate-listdeps", "--all", str(DATA_PATH)) + assert result.success rp_ext_libs = set(realpath(L) for L in EXT_LIBS) - assert set(stdout) == local_libs | rp_ext_libs - assert code == 0 + assert set(_proc_lines(result.stdout)) == local_libs | rp_ext_libs + # Works on wheels as well - code, stdout, stderr = run_command(["delocate-listdeps", PURE_WHEEL]) - assert set(stdout) == set() - code, stdout, stderr = run_command( - ["delocate-listdeps", PURE_WHEEL, plat_wheel.whl] - ) - assert stdout == [ + result = script_runner.run("delocate-listdeps", PURE_WHEEL) + assert result.success + assert result.stdout.strip() == "" + result = script_runner.run("delocate-listdeps", PURE_WHEEL, plat_wheel.whl) + assert result.success + assert _proc_lines(result.stdout) == [ PURE_WHEEL + ":", plat_wheel.whl + ":", plat_wheel.stray_lib, ] + # -d flag (is also --dependency flag) m2 = pjoin("fakepkg1", "subpkg", "module2.abi3.so") - code, stdout, stderr = run_command( - ["delocate-listdeps", "--depending", PURE_WHEEL, plat_wheel.whl] + result = script_runner.run( + "delocate-listdeps", "--depending", PURE_WHEEL, plat_wheel.whl ) - assert stdout == [ + assert result.success + assert _proc_lines(result.stdout) == [ PURE_WHEEL + ":", plat_wheel.whl + ":", plat_wheel.stray_lib + ":", m2, ] + # Can be used with --all - code, stdout, stderr = run_command( - [ - "delocate-listdeps", - "--all", - "--depending", - PURE_WHEEL, - plat_wheel.whl, - ] + result = script_runner.run( + "delocate-listdeps", + "--all", + "--depending", + PURE_WHEEL, + plat_wheel.whl, ) - assert stdout == [ + assert result.success + assert _proc_lines(result.stdout) == [ PURE_WHEEL + ":", plat_wheel.whl + ":", plat_wheel.stray_lib + ":", @@ -164,8 +177,10 @@ def test_listdeps(plat_wheel: PlatWheel) -> None: ] -@pytest.mark.xfail(sys.platform != "darwin", reason="Runs macOS executable.") -def test_path() -> None: +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Runs macOS executable." +) +def test_path(script_runner: ScriptRunner) -> None: # Test path cleaning with InTemporaryDirectory(): # Make a tree; use realpath for OSX /private/var - /var @@ -182,108 +197,116 @@ def test_path() -> None: subprocess.run([stest_lib], check=True) set_install_name(slibc, EXT_LIBS[0], fake_lib) # Check it fixes up correctly - code, stdout, stderr = run_command( - ["delocate-path", "subtree", "subtree2", "-L", "deplibs"] - ) - assert len(os.listdir(pjoin("subtree", "deplibs"))) == 0 + assert script_runner.run( + "delocate-path", "subtree", "subtree2", "-L", "deplibs" + ).success + assert len(os.listdir(Path("subtree", "deplibs"))) == 0 # Check fake libary gets copied and delocated - out_path = pjoin("subtree2", "deplibs") + out_path = Path("subtree2", "deplibs") assert os.listdir(out_path) == ["libfake.dylib"] -@pytest.mark.xfail(sys.platform != "darwin", reason="Needs macOS linkage.") -def test_path_dylibs(): +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_path_dylibs(script_runner: ScriptRunner) -> None: # Test delocate-path with and without dylib extensions with InTemporaryDirectory(): # With 'dylibs-only' - does not inspect non-dylib files liba, bare_b = _make_bare_depends() - out_dypath = pjoin("subtree", "deplibs") - code, stdout, stderr = run_command( - ["delocate-path", "subtree", "-L", "deplibs", "-d"] - ) - assert_equal(len(os.listdir(out_dypath)), 0) - code, stdout, stderr = run_command( - ["delocate-path", "subtree", "-L", "deplibs", "--dylibs-only"] - ) - assert_equal(len(os.listdir(pjoin("subtree", "deplibs"))), 0) + out_dypath = Path("subtree", "deplibs") + assert script_runner.run( + "delocate-path", "subtree", "-L", "deplibs", "-d" + ).success + assert len(os.listdir(out_dypath)) == 0 + assert script_runner.run( + "delocate-path", "subtree", "-L", "deplibs", "--dylibs-only" + ).success + assert len(os.listdir(Path("subtree", "deplibs"))) == 0 # Default - does inspect non-dylib files - code, stdout, stderr = run_command( - ["delocate-path", "subtree", "-L", "deplibs"] - ) - assert_equal(os.listdir(out_dypath), ["liba.dylib"]) + assert script_runner.run( + "delocate-path", "subtree", "-L", "deplibs" + ).success + assert os.listdir(out_dypath) == ["liba.dylib"] -def _check_wheel(wheel_fname, lib_sdir): - wheel_fname = abspath(wheel_fname) +def _check_wheel(wheel_fname: str | Path, lib_sdir: str | Path) -> None: + wheel_fname = Path(wheel_fname).resolve(strict=True) with InTemporaryDirectory(): - zip2dir(wheel_fname, "plat_pkg") - dylibs = pjoin("plat_pkg", "fakepkg1", lib_sdir) - assert_true(exists(dylibs)) - assert_equal(os.listdir(dylibs), ["libextfunc.dylib"]) + zip2dir(str(wheel_fname), "plat_pkg") + dylibs = Path("plat_pkg", "fakepkg1", lib_sdir) + assert dylibs.exists() + assert os.listdir(dylibs) == ["libextfunc.dylib"] -@pytest.mark.xfail(sys.platform != "darwin", reason="Needs macOS linkage.") -def test_wheel(): +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_wheel(script_runner: ScriptRunner) -> None: # Some tests for wheel fixing with InTemporaryDirectory() as tmpdir: # Default in-place fix fixed_wheel, stray_lib = _fixed_wheel(tmpdir) - code, stdout, stderr = run_command(["delocate-wheel", fixed_wheel]) + assert script_runner.run("delocate-wheel", fixed_wheel).success _check_wheel(fixed_wheel, ".dylibs") # Make another copy to test another output directory fixed_wheel, stray_lib = _fixed_wheel(tmpdir) - code, stdout, stderr = run_command( - ["delocate-wheel", "-L", "dynlibs_dir", fixed_wheel] - ) + assert script_runner.run( + "delocate-wheel", "-L", "dynlibs_dir", fixed_wheel + ).success _check_wheel(fixed_wheel, "dynlibs_dir") # Another output directory fixed_wheel, stray_lib = _fixed_wheel(tmpdir) - code, stdout, stderr = run_command( - ["delocate-wheel", "-w", "fixed", fixed_wheel] - ) - _check_wheel(pjoin("fixed", basename(fixed_wheel)), ".dylibs") + assert script_runner.run( + "delocate-wheel", "-w", "fixed", fixed_wheel + ).success + _check_wheel(Path("fixed", basename(fixed_wheel)), ".dylibs") # More than one wheel shutil.copy2(fixed_wheel, "wheel_copy.ext") - code, stdout, stderr = run_command( - ["delocate-wheel", "-w", "fixed2", fixed_wheel, "wheel_copy.ext"] - ) - assert_equal( - stdout, - ["Fixing: " + name for name in (fixed_wheel, "wheel_copy.ext")], + result = script_runner.run( + "delocate-wheel", "-w", "fixed2", fixed_wheel, "wheel_copy.ext" ) - _check_wheel(pjoin("fixed2", basename(fixed_wheel)), ".dylibs") - _check_wheel(pjoin("fixed2", "wheel_copy.ext"), ".dylibs") + assert result.success + assert _proc_lines(result.stdout) == [ + "Fixing: " + name for name in (fixed_wheel, "wheel_copy.ext") + ] + _check_wheel(Path("fixed2", basename(fixed_wheel)), ".dylibs") + _check_wheel(Path("fixed2", "wheel_copy.ext"), ".dylibs") + # Verbose - single wheel - code, stdout, stderr = run_command( - ["delocate-wheel", "-w", "fixed3", fixed_wheel, "-v"] + result = script_runner.run( + "delocate-wheel", "-w", "fixed3", fixed_wheel, "-v" ) - _check_wheel(pjoin("fixed3", basename(fixed_wheel)), ".dylibs") + assert result.success + _check_wheel(Path("fixed3", basename(fixed_wheel)), ".dylibs") wheel_lines1 = [ "Fixing: " + fixed_wheel, "Copied to package .dylibs directory:", stray_lib, ] - assert_equal(stdout, wheel_lines1) - code, stdout, stderr = run_command( - [ - "delocate-wheel", - "-v", - "--wheel-dir", - "fixed4", - fixed_wheel, - "wheel_copy.ext", - ] + assert _proc_lines(result.stdout) == wheel_lines1 + + result = script_runner.run( + "delocate-wheel", + "-v", + "--wheel-dir", + "fixed4", + fixed_wheel, + "wheel_copy.ext", ) + assert result.success wheel_lines2 = [ "Fixing: wheel_copy.ext", "Copied to package .dylibs directory:", stray_lib, ] - assert_equal(stdout, wheel_lines1 + wheel_lines2) + assert _proc_lines(result.stdout) == wheel_lines1 + wheel_lines2 -@pytest.mark.xfail(sys.platform != "darwin", reason="Needs macOS linkage.") -def test_fix_wheel_dylibs(): +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_fix_wheel_dylibs(script_runner: ScriptRunner) -> None: # Check default and non-default search for dynamic libraries with InTemporaryDirectory() as tmpdir: # Default in-place fix @@ -291,26 +314,24 @@ def test_fix_wheel_dylibs(): _rename_module(fixed_wheel, "module.other", "test.whl") shutil.copyfile("test.whl", "test2.whl") # Default is to look in all files and therefore fix - code, stdout, stderr = run_command(["delocate-wheel", "test.whl"]) + assert script_runner.run("delocate-wheel", "test.whl").success _check_wheel("test.whl", ".dylibs") # Can turn this off to only look in dynamic lib exts - code, stdout, stderr = run_command( - ["delocate-wheel", "test2.whl", "-d"] - ) + assert script_runner.run("delocate-wheel", "test2.whl", "-d").success with InWheel("test2.whl"): # No fix - assert_false(exists(pjoin("fakepkg1", ".dylibs"))) + assert not Path("fakepkg1", ".dylibs").exists() -@pytest.mark.xfail(sys.platform != "darwin", reason="Needs macOS linkage.") -def test_fix_wheel_archs() -> None: +@pytest.mark.xfail( # type: ignore[misc] + sys.platform != "darwin", reason="Needs macOS linkage." +) +def test_fix_wheel_archs(script_runner: ScriptRunner) -> None: # Some tests for wheel fixing with InTemporaryDirectory() as tmpdir: # Test check of architectures fixed_wheel, stray_lib = _fixed_wheel(tmpdir) # Fixed wheel, architectures are OK - code, stdout, stderr = run_command( - ["delocate-wheel", fixed_wheel, "-k"] - ) + assert script_runner.run("delocate-wheel", fixed_wheel, "-k").success _check_wheel(fixed_wheel, ".dylibs") # Broken with one architecture removed archs = set(("x86_64", "arm64")) @@ -327,158 +348,158 @@ def _fix_break_fix(arch: Text) -> None: for arch in archs: # Not checked _fix_break(arch) - code, stdout, stderr = run_command(["delocate-wheel", fixed_wheel]) + assert script_runner.run("delocate-wheel", fixed_wheel).success _check_wheel(fixed_wheel, ".dylibs") # Checked _fix_break(arch) - code, stdout, stderr = bytes_runner.run_command( - ["delocate-wheel", fixed_wheel, "--check-archs"], - check_code=False, + result = script_runner.run( + "delocate-wheel", fixed_wheel, "--check-archs" ) - assert_false(code == 0) - stderr_unicode = stderr.decode("latin1").strip() - assert stderr_unicode.startswith("Traceback") + assert result.returncode != 0 + assert result.stderr.startswith("Traceback") assert ( "DelocationError: Some missing architectures in wheel" - in stderr_unicode + in result.stderr ) - assert_equal(stdout.strip(), b"") + assert result.stdout.strip() == "" # Checked, verbose _fix_break(arch) - code, stdout, stderr = bytes_runner.run_command( - ["delocate-wheel", fixed_wheel, "--check-archs", "-v"], - check_code=False, + result = script_runner.run( + "delocate-wheel", fixed_wheel, "--check-archs", "-v" ) - assert_false(code == 0) - stderr = stderr.decode("latin1").strip() - assert "Traceback" in stderr - assert stderr.endswith( + assert result.returncode != 0 + assert "Traceback" in result.stderr + assert result.stderr.endswith( "DelocationError: Some missing architectures in wheel" f"\n{'fakepkg1/subpkg/module2.abi3.so'}" f" needs arch {archs.difference([arch]).pop()}" - f" missing from {stray_lib}" + f" missing from {stray_lib}\n" ) - stdout_unicode = stdout.decode("latin1").strip() - assert stdout_unicode == f"Fixing: {fixed_wheel}" + assert result.stdout == f"Fixing: {fixed_wheel}\n" # Require particular architectures both_archs = "arm64,x86_64" for ok in ("universal2", "arm64", "x86_64", both_archs): _fixed_wheel(tmpdir) - code, stdout, stderr = run_command( - ["delocate-wheel", fixed_wheel, "--require-archs=" + ok] - ) + assert script_runner.run( + "delocate-wheel", fixed_wheel, "--require-archs=" + ok + ).success for arch in archs: other_arch = archs.difference([arch]).pop() for not_ok in ("intel", both_archs, other_arch): _fix_break_fix(arch) - code, stdout, stderr = run_command( - [ + assert ( + script_runner.run( "delocate-wheel", fixed_wheel, "--require-archs=" + not_ok, - ], - check_code=False, + ).returncode + != 0 ) - assert_false(code == 0) -@pytest.mark.xfail(sys.platform == "win32", reason="Can't run scripts.") -def test_fuse_wheels(): +@pytest.mark.xfail( # type: ignore[misc] + sys.platform == "win32", reason="Can't run scripts." +) +def test_fuse_wheels(script_runner: ScriptRunner) -> None: # Some tests for wheel fusing with InTemporaryDirectory(): zip2dir(PLAT_WHEEL, "to_wheel") zip2dir(PLAT_WHEEL, "from_wheel") dir2zip("to_wheel", "to_wheel.whl") dir2zip("from_wheel", "from_wheel.whl") - code, stdout, stderr = run_command( - ["delocate-fuse", "to_wheel.whl", "from_wheel.whl"] - ) - assert_equal(code, 0) + assert script_runner.run( + "delocate-fuse", "to_wheel.whl", "from_wheel.whl" + ).success zip2dir("to_wheel.whl", "to_wheel_fused") assert_same_tree("to_wheel_fused", "from_wheel") # Test output argument os.mkdir("wheels") - code, stdout, stderr = run_command( - ["delocate-fuse", "to_wheel.whl", "from_wheel.whl", "-w", "wheels"] - ) + assert script_runner.run( + "delocate-fuse", "to_wheel.whl", "from_wheel.whl", "-w", "wheels" + ).success zip2dir(pjoin("wheels", "to_wheel.whl"), "to_wheel_refused") assert_same_tree("to_wheel_refused", "from_wheel") -@pytest.mark.xfail(sys.platform == "win32", reason="Can't run scripts.") -def test_patch_wheel(): +@pytest.mark.xfail( # type: ignore[misc] + sys.platform == "win32", reason="Can't run scripts." +) +def test_patch_wheel(script_runner: ScriptRunner) -> None: # Some tests for patching wheel with InTemporaryDirectory(): shutil.copyfile(PURE_WHEEL, "example.whl") # Default is to overwrite input - code, stdout, stderr = run_command( - ["delocate-patch", "example.whl", WHEEL_PATCH] - ) + assert script_runner.run( + "delocate-patch", "example.whl", WHEEL_PATCH + ).success zip2dir("example.whl", "wheel1") - with open(pjoin("wheel1", "fakepkg2", "__init__.py"), "rt") as fobj: - assert_equal(fobj.read(), 'print("Am in init")\n') + assert ( + Path("wheel1", "fakepkg2", "__init__.py").read_text() + == 'print("Am in init")\n' + ) # Pass output directory shutil.copyfile(PURE_WHEEL, "example.whl") - code, stdout, stderr = run_command( - ["delocate-patch", "example.whl", WHEEL_PATCH, "-w", "wheels"] - ) + assert script_runner.run( + "delocate-patch", "example.whl", WHEEL_PATCH, "-w", "wheels" + ).success zip2dir(pjoin("wheels", "example.whl"), "wheel2") - with open(pjoin("wheel2", "fakepkg2", "__init__.py"), "rt") as fobj: - assert_equal(fobj.read(), 'print("Am in init")\n') + assert ( + Path("wheel2", "fakepkg2", "__init__.py").read_text() + == 'print("Am in init")\n' + ) # Bad patch fails shutil.copyfile(PURE_WHEEL, "example.whl") - assert_raises( - RuntimeError, - run_command, - ["delocate-patch", "example.whl", WHEEL_PATCH_BAD], - ) + assert not script_runner.run( + "delocate-patch", "example.whl", WHEEL_PATCH_BAD + ).success -@pytest.mark.xfail(sys.platform == "win32", reason="Can't run scripts.") -def test_add_platforms() -> None: +@pytest.mark.xfail( # type: ignore[misc] + sys.platform == "win32", reason="Can't run scripts." +) +def test_add_platforms(script_runner: ScriptRunner) -> None: # Check adding platform to wheel name and tag section assert_winfo_similar(PLAT_WHEEL, EXP_ITEMS, drop_version=False) with InTemporaryDirectory() as tmpdir: # First wheel needs proper wheel filename for later unpack test out_fname = basename(PURE_WHEEL) # Need to specify at least one platform - with pytest.raises(RuntimeError): - run_command(["delocate-addplat", PURE_WHEEL, "-w", tmpdir]) - plat_args = ["-p", EXTRA_PLATS[0], "--plat-tag", EXTRA_PLATS[1]] + assert not script_runner.run( + "delocate-addplat", PURE_WHEEL, "-w", tmpdir + ).success + plat_args = ("-p", EXTRA_PLATS[0], "--plat-tag", EXTRA_PLATS[1]) # Can't add platforms to a pure wheel - with pytest.raises(RuntimeError): - run_command( - ["delocate-addplat", PURE_WHEEL, "-w", tmpdir] + plat_args - ) + assert not script_runner.run( + "delocate-addplat", PURE_WHEEL, "-w", tmpdir, *plat_args + ).success assert not exists(out_fname) # Error raised (as above) unless ``--skip-error`` flag set - code, stdout, stderr = run_command( - ["delocate-addplat", PURE_WHEEL, "-w", tmpdir, "-k"] + plat_args - ) + assert script_runner.run( + "delocate-addplat", PURE_WHEEL, "-w", tmpdir, "-k", *plat_args + ).success # Still doesn't do anything though assert not exists(out_fname) # Works for plat_wheel out_fname = ".".join( (splitext(basename(PLAT_WHEEL))[0],) + EXTRA_PLATS + ("whl",) ) - code, stdout, stderr = run_command( - ["delocate-addplat", PLAT_WHEEL, "-w", tmpdir] + plat_args - ) - assert isfile(out_fname) + assert script_runner.run( + "delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args + ).success + assert Path(out_fname).is_file() assert_winfo_similar(out_fname, EXTRA_EXPS) - # If wheel exists (as it does) then raise error - with pytest.raises(RuntimeError): - run_command( - ["delocate-addplat", PLAT_WHEEL, "-w", tmpdir] + plat_args - ) + # If wheel exists (as it does) then fail + assert not script_runner.run( + "delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args + ).success # Unless clobber is set - code, stdout, stderr = run_command( - ["delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir] + plat_args - ) + assert script_runner.run( + "delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, *plat_args + ).success # Can also specify platform tags via --osx-ver flags - code, stdout, stderr = run_command( - ["delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, "-x", "10_9"] - ) + assert script_runner.run( + "delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, "-x", "10_9" + ).success assert_winfo_similar(out_fname, EXTRA_EXPS) # Can mix plat_tag and osx_ver extra_extra = ("macosx_10_12_universal2", "macosx_10_12_x86_64") @@ -491,34 +512,32 @@ def test_add_platforms() -> None: extra_big_exp = EXTRA_EXPS + [ ("Tag", "{pyver}-{abi}-" + plat) for plat in extra_extra ] - code, stdout, stderr = run_command( - [ - "delocate-addplat", - PLAT_WHEEL, - "-w", - tmpdir, - "-x", - "10_12", - "-d", - "universal2", - ] - + plat_args - ) + assert script_runner.run( + "delocate-addplat", + PLAT_WHEEL, + "-w", + tmpdir, + "-x", + "10_12", + "-d", + "universal2", + *plat_args, + ).success assert_winfo_similar(out_big_fname, extra_big_exp) # Default is to write into directory of wheel os.mkdir("wheels") shutil.copy2(PLAT_WHEEL, "wheels") local_plat = pjoin("wheels", basename(PLAT_WHEEL)) local_out = pjoin("wheels", out_fname) - code, stdout, stderr = run_command( - ["delocate-addplat", local_plat] + plat_args - ) + assert script_runner.run( + "delocate-addplat", local_plat, *plat_args + ).success assert exists(local_out) # With rm_orig flag, delete original unmodified wheel os.unlink(local_out) - code, stdout, stderr = run_command( - ["delocate-addplat", "-r", local_plat] + plat_args - ) + assert script_runner.run( + "delocate-addplat", "-r", local_plat, *plat_args + ).success assert not exists(local_plat) assert exists(local_out) # Copy original back again @@ -526,22 +545,23 @@ def test_add_platforms() -> None: # If platforms already present, don't write more res = sorted(os.listdir("wheels")) assert_winfo_similar(local_out, EXTRA_EXPS) - code, stdout, stderr = run_command( - ["delocate-addplat", local_out, "--clobber"] + plat_args - ) + assert script_runner.run( + "delocate-addplat", local_out, "--clobber", *plat_args + ).success assert sorted(os.listdir("wheels")) == res assert_winfo_similar(local_out, EXTRA_EXPS) # The wheel doesn't get deleted output name same as input, as here - code, stdout, stderr = run_command( - ["delocate-addplat", local_out, "-r", "--clobber"] + plat_args - ) + assert script_runner.run( + "delocate-addplat", local_out, "-r", "--clobber", *plat_args + ).success assert sorted(os.listdir("wheels")) == res # But adds WHEEL tags if missing, even if file name is OK shutil.copy2(local_plat, local_out) with pytest.raises(AssertionError): assert_winfo_similar(local_out, EXTRA_EXPS) - code, stdout, stderr = run_command( - ["delocate-addplat", local_out, "--clobber"] + plat_args - ) + assert script_runner.run( + "delocate-addplat", local_out, "--clobber", *plat_args + ).success assert sorted(os.listdir("wheels")) == res assert_winfo_similar(local_out, EXTRA_EXPS) + assert_winfo_similar(local_out, EXTRA_EXPS) diff --git a/delocate/tests/test_wheelies.py b/delocate/tests/test_wheelies.py index fabe189d..8477dabd 100644 --- a/delocate/tests/test_wheelies.py +++ b/delocate/tests/test_wheelies.py @@ -1,4 +1,5 @@ """ Direct tests of fixes to wheels """ +from __future__ import annotations import os import shutil @@ -10,6 +11,7 @@ from os.path import abspath, basename, exists, isdir from os.path import join as pjoin from os.path import realpath +from pathlib import Path from subprocess import check_call from typing import NamedTuple @@ -83,7 +85,7 @@ def test_fix_pure_python(): assert_false(exists(pjoin("pure_pkg", "fakepkg2", ".dylibs"))) -def _fixed_wheel(out_path): +def _fixed_wheel(out_path: str | Path) -> tuple[str, str]: wheel_base = basename(PLAT_WHEEL) with InGivenDirectory(out_path): zip2dir(PLAT_WHEEL, "_plat_pkg") @@ -99,12 +101,13 @@ def _fixed_wheel(out_path): return pjoin(out_path, wheel_base), stray_lib -def _rename_module(in_wheel, mod_fname, out_wheel): - # Rename module with library dependency in wheel - with InWheel(in_wheel, out_wheel): - mod_dir = pjoin("fakepkg1", "subpkg") - os.rename(pjoin(mod_dir, "module2.abi3.so"), pjoin(mod_dir, mod_fname)) - return out_wheel +def _rename_module( + in_wheel: str | Path, mod_fname: str | Path, out_wheel: str | Path +) -> None: + """Rename a module with library dependency in a wheel.""" + with InWheel(str(in_wheel), str(out_wheel)): + mod_dir = Path("fakepkg1", "subpkg") + os.rename(Path(mod_dir, "module2.abi3.so"), Path(mod_dir, mod_fname)) @pytest.mark.xfail(sys.platform != "darwin", reason="otool") @@ -219,13 +222,14 @@ def func(fn): ) -def _thin_lib(stray_lib, arch): +def _thin_lib(stray_lib: str | Path, arch: str) -> None: + stray_lib = str(stray_lib) check_call(["lipo", "-thin", arch, stray_lib, "-output", stray_lib]) -def _thin_mod(wheel, arch): +def _thin_mod(wheel: str | Path, arch: str) -> None: with InWheel(wheel, wheel): - mod_fname = pjoin("fakepkg1", "subpkg", "module2.abi3.so") + mod_fname = str(Path("fakepkg1", "subpkg", "module2.abi3.so")) check_call(["lipo", "-thin", arch, mod_fname, "-output", mod_fname]) diff --git a/pyproject.toml b/pyproject.toml index 363312b8..ca45fa93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ line_length = 80 [tool.pytest.ini_options] minversion = "6.0" -required_plugins = ["pytest-cov"] +required_plugins = ["pytest-console-scripts", "pytest-cov"] testpaths = ["delocate/"] addopts = ["--doctest-modules", "--cov=delocate", "--cov-config=.coveragerc"] log_file_level = "DEBUG" diff --git a/test-requirements.txt b/test-requirements.txt index 88252fda..6c128c45 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,5 @@ # Requirements for tests wheel pytest +pytest-console-scripts pytest-cov From 671ee1c14b3717bebeb1dd9e302b0bb7e55b5814 Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Mon, 22 May 2023 17:56:07 -0700 Subject: [PATCH 2/3] Upgrade pytest-console-scripts. The new API works more like subprocess.run. Also has its own type hints now. --- .github/workflows/python-package.yml | 4 +- delocate/tests/test_scripts.py | 305 ++++++++++++++------------- pyproject.toml | 2 +- test-requirements.txt | 2 +- 4 files changed, 163 insertions(+), 150 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index e0c79338..6a2194bf 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -38,8 +38,8 @@ jobs: pip install mypy - name: Install Delocate dependencies. run: | - pip install pytest - pip install -e . + pip install --requirement test-requirements.txt + pip install --editable . - name: MyPy uses: liskin/gh-problem-matcher-wrap@v2 with: diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index 0561f8aa..40166ced 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -16,10 +16,10 @@ from os.path import join as pjoin from os.path import realpath, splitext from pathlib import Path -from typing import Any, Text +from typing import Text import pytest -from typing_extensions import Protocol +from pytest_console_scripts import ScriptRunner from ..tmpdirs import InGivenDirectory, InTemporaryDirectory from ..tools import dir2zip, set_install_name, zip2dir @@ -66,22 +66,6 @@ def _proc_lines(in_str: str) -> list[str]: return [line.strip() for line in lines if line.strip() != ""] -class RunResult(Protocol): - """Result of running a script.""" - - success: bool - returncode: int - stdout: str - stderr: str - - -class ScriptRunner(Protocol): - """Protocol for pytest-console-scripts ScriptRunner.""" - - def run(self, command: str, *arguments: str, **options: Any) -> RunResult: - ... - - @pytest.mark.xfail( # type: ignore[misc] sys.platform != "darwin", reason="Needs macOS linkage." ) @@ -95,20 +79,21 @@ def test_listdeps(plat_wheel: PlatWheel, script_runner: ScriptRunner) -> None: } # single path, with libs with InGivenDirectory(DATA_PATH): - result = script_runner.run("delocate-listdeps", str(DATA_PATH)) - assert result.success + result = script_runner.run( + "delocate-listdeps", str(DATA_PATH), check=True + ) assert set(_proc_lines(result.stdout)) == local_libs # single path, no libs with InTemporaryDirectory(): zip2dir(PURE_WHEEL, "pure") - result = script_runner.run("delocate-listdeps", "pure") - assert result.success + result = script_runner.run(["delocate-listdeps", "pure"], check=True) assert result.stdout.strip() == "" # Multiple paths one with libs zip2dir(plat_wheel.whl, "plat") - result = script_runner.run("delocate-listdeps", "pure", "plat") - assert result.success + result = script_runner.run( + ["delocate-listdeps", "pure", "plat"], check=True + ) assert _proc_lines(result.stdout) == [ "pure:", "plat:", @@ -116,8 +101,9 @@ def test_listdeps(plat_wheel: PlatWheel, script_runner: ScriptRunner) -> None: ] # With -d flag, get list of dependending modules - result = script_runner.run("delocate-listdeps", "-d", "pure", "plat") - assert result.success + result = script_runner.run( + ["delocate-listdeps", "-d", "pure", "plat"], check=True + ) assert _proc_lines(result.stdout) == [ "pure:", "plat:", @@ -127,17 +113,18 @@ def test_listdeps(plat_wheel: PlatWheel, script_runner: ScriptRunner) -> None: # With --all flag, get all dependencies with InGivenDirectory(DATA_PATH): - result = script_runner.run("delocate-listdeps", "--all", str(DATA_PATH)) - assert result.success + result = script_runner.run( + ["delocate-listdeps", "--all", DATA_PATH], check=True + ) rp_ext_libs = set(realpath(L) for L in EXT_LIBS) assert set(_proc_lines(result.stdout)) == local_libs | rp_ext_libs # Works on wheels as well - result = script_runner.run("delocate-listdeps", PURE_WHEEL) - assert result.success + result = script_runner.run(["delocate-listdeps", PURE_WHEEL], check=True) assert result.stdout.strip() == "" - result = script_runner.run("delocate-listdeps", PURE_WHEEL, plat_wheel.whl) - assert result.success + result = script_runner.run( + ["delocate-listdeps", PURE_WHEEL, plat_wheel.whl], check=True + ) assert _proc_lines(result.stdout) == [ PURE_WHEEL + ":", plat_wheel.whl + ":", @@ -147,9 +134,9 @@ def test_listdeps(plat_wheel: PlatWheel, script_runner: ScriptRunner) -> None: # -d flag (is also --dependency flag) m2 = pjoin("fakepkg1", "subpkg", "module2.abi3.so") result = script_runner.run( - "delocate-listdeps", "--depending", PURE_WHEEL, plat_wheel.whl + ["delocate-listdeps", "--depending", PURE_WHEEL, plat_wheel.whl], + check=True, ) - assert result.success assert _proc_lines(result.stdout) == [ PURE_WHEEL + ":", plat_wheel.whl + ":", @@ -159,13 +146,15 @@ def test_listdeps(plat_wheel: PlatWheel, script_runner: ScriptRunner) -> None: # Can be used with --all result = script_runner.run( - "delocate-listdeps", - "--all", - "--depending", - PURE_WHEEL, - plat_wheel.whl, + [ + "delocate-listdeps", + "--all", + "--depending", + PURE_WHEEL, + plat_wheel.whl, + ], + check=True, ) - assert result.success assert _proc_lines(result.stdout) == [ PURE_WHEEL + ":", plat_wheel.whl + ":", @@ -197,9 +186,10 @@ def test_path(script_runner: ScriptRunner) -> None: subprocess.run([stest_lib], check=True) set_install_name(slibc, EXT_LIBS[0], fake_lib) # Check it fixes up correctly - assert script_runner.run( - "delocate-path", "subtree", "subtree2", "-L", "deplibs" - ).success + script_runner.run( + ["delocate-path", "subtree", "subtree2", "-L", "deplibs"], + check=True, + ) assert len(os.listdir(Path("subtree", "deplibs"))) == 0 # Check fake libary gets copied and delocated out_path = Path("subtree2", "deplibs") @@ -215,18 +205,19 @@ def test_path_dylibs(script_runner: ScriptRunner) -> None: # With 'dylibs-only' - does not inspect non-dylib files liba, bare_b = _make_bare_depends() out_dypath = Path("subtree", "deplibs") - assert script_runner.run( - "delocate-path", "subtree", "-L", "deplibs", "-d" - ).success + script_runner.run( + ["delocate-path", "subtree", "-L", "deplibs", "-d"], check=True + ) assert len(os.listdir(out_dypath)) == 0 - assert script_runner.run( - "delocate-path", "subtree", "-L", "deplibs", "--dylibs-only" - ).success + script_runner.run( + ["delocate-path", "subtree", "-L", "deplibs", "--dylibs-only"], + check=True, + ) assert len(os.listdir(Path("subtree", "deplibs"))) == 0 # Default - does inspect non-dylib files - assert script_runner.run( - "delocate-path", "subtree", "-L", "deplibs" - ).success + script_runner.run( + ["delocate-path", "subtree", "-L", "deplibs"], check=True + ) assert os.listdir(out_dypath) == ["liba.dylib"] @@ -247,26 +238,26 @@ def test_wheel(script_runner: ScriptRunner) -> None: with InTemporaryDirectory() as tmpdir: # Default in-place fix fixed_wheel, stray_lib = _fixed_wheel(tmpdir) - assert script_runner.run("delocate-wheel", fixed_wheel).success + script_runner.run(["delocate-wheel", fixed_wheel], check=True) _check_wheel(fixed_wheel, ".dylibs") # Make another copy to test another output directory fixed_wheel, stray_lib = _fixed_wheel(tmpdir) - assert script_runner.run( - "delocate-wheel", "-L", "dynlibs_dir", fixed_wheel - ).success + script_runner.run( + ["delocate-wheel", "-L", "dynlibs_dir", fixed_wheel], check=True + ) _check_wheel(fixed_wheel, "dynlibs_dir") # Another output directory fixed_wheel, stray_lib = _fixed_wheel(tmpdir) - assert script_runner.run( - "delocate-wheel", "-w", "fixed", fixed_wheel - ).success + script_runner.run( + ["delocate-wheel", "-w", "fixed", fixed_wheel], check=True + ) _check_wheel(Path("fixed", basename(fixed_wheel)), ".dylibs") # More than one wheel shutil.copy2(fixed_wheel, "wheel_copy.ext") result = script_runner.run( - "delocate-wheel", "-w", "fixed2", fixed_wheel, "wheel_copy.ext" + ["delocate-wheel", "-w", "fixed2", fixed_wheel, "wheel_copy.ext"], + check=True, ) - assert result.success assert _proc_lines(result.stdout) == [ "Fixing: " + name for name in (fixed_wheel, "wheel_copy.ext") ] @@ -275,9 +266,8 @@ def test_wheel(script_runner: ScriptRunner) -> None: # Verbose - single wheel result = script_runner.run( - "delocate-wheel", "-w", "fixed3", fixed_wheel, "-v" + ["delocate-wheel", "-w", "fixed3", fixed_wheel, "-v"], check=True ) - assert result.success _check_wheel(Path("fixed3", basename(fixed_wheel)), ".dylibs") wheel_lines1 = [ "Fixing: " + fixed_wheel, @@ -287,14 +277,16 @@ def test_wheel(script_runner: ScriptRunner) -> None: assert _proc_lines(result.stdout) == wheel_lines1 result = script_runner.run( - "delocate-wheel", - "-v", - "--wheel-dir", - "fixed4", - fixed_wheel, - "wheel_copy.ext", - ) - assert result.success + [ + "delocate-wheel", + "-v", + "--wheel-dir", + "fixed4", + fixed_wheel, + "wheel_copy.ext", + ], + check=True, + ) wheel_lines2 = [ "Fixing: wheel_copy.ext", "Copied to package .dylibs directory:", @@ -314,10 +306,10 @@ def test_fix_wheel_dylibs(script_runner: ScriptRunner) -> None: _rename_module(fixed_wheel, "module.other", "test.whl") shutil.copyfile("test.whl", "test2.whl") # Default is to look in all files and therefore fix - assert script_runner.run("delocate-wheel", "test.whl").success + script_runner.run(["delocate-wheel", "test.whl"], check=True) _check_wheel("test.whl", ".dylibs") # Can turn this off to only look in dynamic lib exts - assert script_runner.run("delocate-wheel", "test2.whl", "-d").success + script_runner.run(["delocate-wheel", "test2.whl", "-d"], check=True) with InWheel("test2.whl"): # No fix assert not Path("fakepkg1", ".dylibs").exists() @@ -331,7 +323,7 @@ def test_fix_wheel_archs(script_runner: ScriptRunner) -> None: # Test check of architectures fixed_wheel, stray_lib = _fixed_wheel(tmpdir) # Fixed wheel, architectures are OK - assert script_runner.run("delocate-wheel", fixed_wheel, "-k").success + script_runner.run(["delocate-wheel", fixed_wheel, "-k"], check=True) _check_wheel(fixed_wheel, ".dylibs") # Broken with one architecture removed archs = set(("x86_64", "arm64")) @@ -348,12 +340,12 @@ def _fix_break_fix(arch: Text) -> None: for arch in archs: # Not checked _fix_break(arch) - assert script_runner.run("delocate-wheel", fixed_wheel).success + script_runner.run(["delocate-wheel", fixed_wheel], check=True) _check_wheel(fixed_wheel, ".dylibs") # Checked _fix_break(arch) result = script_runner.run( - "delocate-wheel", fixed_wheel, "--check-archs" + ["delocate-wheel", fixed_wheel, "--check-archs"] ) assert result.returncode != 0 assert result.stderr.startswith("Traceback") @@ -365,7 +357,7 @@ def _fix_break_fix(arch: Text) -> None: # Checked, verbose _fix_break(arch) result = script_runner.run( - "delocate-wheel", fixed_wheel, "--check-archs", "-v" + ["delocate-wheel", fixed_wheel, "--check-archs", "-v"] ) assert result.returncode != 0 assert "Traceback" in result.stderr @@ -380,21 +372,22 @@ def _fix_break_fix(arch: Text) -> None: both_archs = "arm64,x86_64" for ok in ("universal2", "arm64", "x86_64", both_archs): _fixed_wheel(tmpdir) - assert script_runner.run( - "delocate-wheel", fixed_wheel, "--require-archs=" + ok - ).success + script_runner.run( + ["delocate-wheel", fixed_wheel, "--require-archs=" + ok], + check=True, + ) for arch in archs: other_arch = archs.difference([arch]).pop() for not_ok in ("intel", both_archs, other_arch): _fix_break_fix(arch) - assert ( - script_runner.run( + result = script_runner.run( + [ "delocate-wheel", fixed_wheel, "--require-archs=" + not_ok, - ).returncode - != 0 + ], ) + assert result.returncode != 0 @pytest.mark.xfail( # type: ignore[misc] @@ -407,16 +400,17 @@ def test_fuse_wheels(script_runner: ScriptRunner) -> None: zip2dir(PLAT_WHEEL, "from_wheel") dir2zip("to_wheel", "to_wheel.whl") dir2zip("from_wheel", "from_wheel.whl") - assert script_runner.run( - "delocate-fuse", "to_wheel.whl", "from_wheel.whl" - ).success + script_runner.run( + ["delocate-fuse", "to_wheel.whl", "from_wheel.whl"], check=True + ) zip2dir("to_wheel.whl", "to_wheel_fused") assert_same_tree("to_wheel_fused", "from_wheel") # Test output argument os.mkdir("wheels") - assert script_runner.run( - "delocate-fuse", "to_wheel.whl", "from_wheel.whl", "-w", "wheels" - ).success + script_runner.run( + ["delocate-fuse", "to_wheel.whl", "from_wheel.whl", "-w", "wheels"], + check=True, + ) zip2dir(pjoin("wheels", "to_wheel.whl"), "to_wheel_refused") assert_same_tree("to_wheel_refused", "from_wheel") @@ -429,9 +423,9 @@ def test_patch_wheel(script_runner: ScriptRunner) -> None: with InTemporaryDirectory(): shutil.copyfile(PURE_WHEEL, "example.whl") # Default is to overwrite input - assert script_runner.run( - "delocate-patch", "example.whl", WHEEL_PATCH - ).success + script_runner.run( + ["delocate-patch", "example.whl", WHEEL_PATCH], check=True + ) zip2dir("example.whl", "wheel1") assert ( Path("wheel1", "fakepkg2", "__init__.py").read_text() @@ -439,9 +433,10 @@ def test_patch_wheel(script_runner: ScriptRunner) -> None: ) # Pass output directory shutil.copyfile(PURE_WHEEL, "example.whl") - assert script_runner.run( - "delocate-patch", "example.whl", WHEEL_PATCH, "-w", "wheels" - ).success + script_runner.run( + ["delocate-patch", "example.whl", WHEEL_PATCH, "-w", "wheels"], + check=True, + ) zip2dir(pjoin("wheels", "example.whl"), "wheel2") assert ( Path("wheel2", "fakepkg2", "__init__.py").read_text() @@ -449,9 +444,10 @@ def test_patch_wheel(script_runner: ScriptRunner) -> None: ) # Bad patch fails shutil.copyfile(PURE_WHEEL, "example.whl") - assert not script_runner.run( - "delocate-patch", "example.whl", WHEEL_PATCH_BAD - ).success + result = script_runner.run( + ["delocate-patch", "example.whl", WHEEL_PATCH_BAD] + ) + assert result.returncode != 0 @pytest.mark.xfail( # type: ignore[misc] @@ -464,42 +460,55 @@ def test_add_platforms(script_runner: ScriptRunner) -> None: # First wheel needs proper wheel filename for later unpack test out_fname = basename(PURE_WHEEL) # Need to specify at least one platform - assert not script_runner.run( - "delocate-addplat", PURE_WHEEL, "-w", tmpdir - ).success + assert ( + script_runner.run( + ["delocate-addplat", PURE_WHEEL, "-w", tmpdir] + ).returncode + != 0 + ) plat_args = ("-p", EXTRA_PLATS[0], "--plat-tag", EXTRA_PLATS[1]) # Can't add platforms to a pure wheel - assert not script_runner.run( - "delocate-addplat", PURE_WHEEL, "-w", tmpdir, *plat_args - ).success + assert ( + script_runner.run( + ["delocate-addplat", PURE_WHEEL, "-w", tmpdir, *plat_args] + ).returncode + != 0 + ) assert not exists(out_fname) # Error raised (as above) unless ``--skip-error`` flag set - assert script_runner.run( - "delocate-addplat", PURE_WHEEL, "-w", tmpdir, "-k", *plat_args - ).success + script_runner.run( + ["delocate-addplat", PURE_WHEEL, "-w", tmpdir, "-k", *plat_args], + check=True, + ) # Still doesn't do anything though assert not exists(out_fname) # Works for plat_wheel out_fname = ".".join( (splitext(basename(PLAT_WHEEL))[0],) + EXTRA_PLATS + ("whl",) ) - assert script_runner.run( - "delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args - ).success + script_runner.run( + ["delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args], + check=True, + ) assert Path(out_fname).is_file() assert_winfo_similar(out_fname, EXTRA_EXPS) # If wheel exists (as it does) then fail - assert not script_runner.run( - "delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args - ).success + assert ( + script_runner.run( + ["delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args] + ).returncode + != 0 + ) # Unless clobber is set - assert script_runner.run( - "delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, *plat_args - ).success + script_runner.run( + ["delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, *plat_args], + check=True, + ) # Can also specify platform tags via --osx-ver flags - assert script_runner.run( - "delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, "-x", "10_9" - ).success + script_runner.run( + ["delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, "-x", "10_9"], + check=True, + ) assert_winfo_similar(out_fname, EXTRA_EXPS) # Can mix plat_tag and osx_ver extra_extra = ("macosx_10_12_universal2", "macosx_10_12_x86_64") @@ -512,32 +521,35 @@ def test_add_platforms(script_runner: ScriptRunner) -> None: extra_big_exp = EXTRA_EXPS + [ ("Tag", "{pyver}-{abi}-" + plat) for plat in extra_extra ] - assert script_runner.run( - "delocate-addplat", - PLAT_WHEEL, - "-w", - tmpdir, - "-x", - "10_12", - "-d", - "universal2", - *plat_args, - ).success + script_runner.run( + [ + "delocate-addplat", + PLAT_WHEEL, + "-w", + tmpdir, + "-x", + "10_12", + "-d", + "universal2", + *plat_args, + ], + check=True, + ) assert_winfo_similar(out_big_fname, extra_big_exp) # Default is to write into directory of wheel os.mkdir("wheels") shutil.copy2(PLAT_WHEEL, "wheels") local_plat = pjoin("wheels", basename(PLAT_WHEEL)) local_out = pjoin("wheels", out_fname) - assert script_runner.run( - "delocate-addplat", local_plat, *plat_args - ).success + script_runner.run( + ["delocate-addplat", local_plat, *plat_args], check=True + ) assert exists(local_out) # With rm_orig flag, delete original unmodified wheel os.unlink(local_out) - assert script_runner.run( - "delocate-addplat", "-r", local_plat, *plat_args - ).success + script_runner.run( + ["delocate-addplat", "-r", local_plat, *plat_args], check=True + ) assert not exists(local_plat) assert exists(local_out) # Copy original back again @@ -545,23 +557,24 @@ def test_add_platforms(script_runner: ScriptRunner) -> None: # If platforms already present, don't write more res = sorted(os.listdir("wheels")) assert_winfo_similar(local_out, EXTRA_EXPS) - assert script_runner.run( - "delocate-addplat", local_out, "--clobber", *plat_args - ).success + script_runner.run( + ["delocate-addplat", local_out, "--clobber", *plat_args], check=True + ) assert sorted(os.listdir("wheels")) == res assert_winfo_similar(local_out, EXTRA_EXPS) # The wheel doesn't get deleted output name same as input, as here - assert script_runner.run( - "delocate-addplat", local_out, "-r", "--clobber", *plat_args - ).success + script_runner.run( + ["delocate-addplat", local_out, "-r", "--clobber", *plat_args], + check=True, + ) assert sorted(os.listdir("wheels")) == res # But adds WHEEL tags if missing, even if file name is OK shutil.copy2(local_plat, local_out) with pytest.raises(AssertionError): assert_winfo_similar(local_out, EXTRA_EXPS) - assert script_runner.run( - "delocate-addplat", local_out, "--clobber", *plat_args - ).success + script_runner.run( + ["delocate-addplat", local_out, "--clobber", *plat_args], check=True + ) assert sorted(os.listdir("wheels")) == res assert_winfo_similar(local_out, EXTRA_EXPS) assert_winfo_similar(local_out, EXTRA_EXPS) diff --git a/pyproject.toml b/pyproject.toml index ca45fa93..77a0d9b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,7 @@ line_length = 80 [tool.pytest.ini_options] minversion = "6.0" -required_plugins = ["pytest-console-scripts", "pytest-cov"] +required_plugins = ["pytest-console-scripts>=1.4.0", "pytest-cov"] testpaths = ["delocate/"] addopts = ["--doctest-modules", "--cov=delocate", "--cov-config=.coveragerc"] log_file_level = "DEBUG" diff --git a/test-requirements.txt b/test-requirements.txt index 6c128c45..56f63600 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,5 +1,5 @@ # Requirements for tests wheel pytest -pytest-console-scripts +pytest-console-scripts~=1.4 pytest-cov From 9b07f10644a3e1c6dad3414e21c262d29b910579 Mon Sep 17 00:00:00 2001 From: Kyle Benesch <4b796c65+github@gmail.com> Date: Tue, 23 May 2023 05:02:02 -0700 Subject: [PATCH 3/3] Replace script return code check with pytest.raises. --- delocate/tests/test_scripts.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/delocate/tests/test_scripts.py b/delocate/tests/test_scripts.py index 40166ced..a99008a9 100644 --- a/delocate/tests/test_scripts.py +++ b/delocate/tests/test_scripts.py @@ -460,20 +460,17 @@ def test_add_platforms(script_runner: ScriptRunner) -> None: # First wheel needs proper wheel filename for later unpack test out_fname = basename(PURE_WHEEL) # Need to specify at least one platform - assert ( + with pytest.raises(subprocess.CalledProcessError): script_runner.run( - ["delocate-addplat", PURE_WHEEL, "-w", tmpdir] - ).returncode - != 0 - ) + ["delocate-addplat", PURE_WHEEL, "-w", tmpdir], check=True + ) plat_args = ("-p", EXTRA_PLATS[0], "--plat-tag", EXTRA_PLATS[1]) # Can't add platforms to a pure wheel - assert ( + with pytest.raises(subprocess.CalledProcessError): script_runner.run( - ["delocate-addplat", PURE_WHEEL, "-w", tmpdir, *plat_args] - ).returncode - != 0 - ) + ["delocate-addplat", PURE_WHEEL, "-w", tmpdir, *plat_args], + check=True, + ) assert not exists(out_fname) # Error raised (as above) unless ``--skip-error`` flag set script_runner.run( @@ -493,12 +490,11 @@ def test_add_platforms(script_runner: ScriptRunner) -> None: assert Path(out_fname).is_file() assert_winfo_similar(out_fname, EXTRA_EXPS) # If wheel exists (as it does) then fail - assert ( + with pytest.raises(subprocess.CalledProcessError): script_runner.run( - ["delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args] - ).returncode - != 0 - ) + ["delocate-addplat", PLAT_WHEEL, "-w", tmpdir, *plat_args], + check=True, + ) # Unless clobber is set script_runner.run( ["delocate-addplat", PLAT_WHEEL, "-c", "-w", tmpdir, *plat_args],