1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
import configparser
import contextlib
import functools
import os
import pathlib
import re
import subprocess
import sys
from configparser import ConfigParser
import pytest
from packaging.version import parse as ver
PYTHON_VERSION_DOT = f"{sys.version_info[0]}.{sys.version_info[1]}"
PYTHON_VERSION_NODOT = f"{sys.version_info[0]}{sys.version_info[1]}"
NATIVE_TOXENV = f"py{PYTHON_VERSION_NODOT}"
NATIVE_SITE_PACKAGES = f"lib/python{PYTHON_VERSION_DOT}/site-packages"
NATIVE_EXECUTABLE = str(pathlib.Path(sys.executable).resolve())
FIXTURES_DIR = pathlib.Path(__file__).parent / "fixtures"
DOT_TOX = pathlib.Path("./.tox")
def _exec_prefix(executable):
"""Returns sys.exec_prefix for the given executable"""
cmd = (executable, "-c", "import sys; print(sys.exec_prefix)")
return subprocess.check_output(cmd, encoding="utf-8").strip()
NATIVE_EXEC_PREFIX = _exec_prefix(NATIVE_EXECUTABLE)
NATIVE_EXEC_PREFIX_MSG = f"{NATIVE_EXEC_PREFIX} is the exec_prefix"
def tox(*args, quiet=True, **kwargs):
kwargs.setdefault("encoding", "utf-8")
kwargs.setdefault("stdout", subprocess.PIPE)
kwargs.setdefault("stderr", subprocess.PIPE)
kwargs.setdefault("check", True)
kwargs.setdefault("cwd", os.getcwd())
q = ("-q",) if quiet else ()
env = dict(os.environ)
env.pop("TOX_WORK_DIR", None)
kwargs["env"] = {**env, **kwargs.get("env", {})}
try:
print("current", os.getcwd(), "running in", kwargs["cwd"])
cp = subprocess.run((sys.executable, "-m", "tox") + q + args, **kwargs)
except subprocess.CalledProcessError as e:
print(e.stdout, file=sys.stdout)
print(e.stderr, file=sys.stderr)
raise
print(cp.stdout, file=sys.stdout)
print(cp.stderr, file=sys.stderr)
return cp
TOX_VERSION = ver(tox("--version").stdout.split(" ")[0].split("+")[0])
TOX4 = TOX_VERSION.major == 4
@contextlib.contextmanager
def modify_config(tox_ini_path):
"""Context manager that allows modifying the given Tox config file
A statement like::
with prepare_config(projdir) as config:
will make `config` a ConfigParser instance that is saved at the end
of the `with` block.
"""
config = configparser.ConfigParser()
config.read(tox_ini_path)
yield config
with open(tox_ini_path, "w") as tox_ini_file:
config.write(tox_ini_file)
@functools.lru_cache(maxsize=8)
def is_available(python):
try:
subprocess.run((python, "--version"))
except FileNotFoundError:
return False
return True
@functools.lru_cache()
def drop_unsupported_pythons(envlist):
return envlist[len("py36,py37,"):] if TOX4 else envlist
@functools.lru_cache()
def envs_from_tox_ini():
cp = ConfigParser()
cp.read(FIXTURES_DIR / "tox.ini")
envlist = drop_unsupported_pythons(cp["tox"]["envlist"])
return envlist.split(",")
def tox_footer(envs=None, spaces=8):
if envs is None:
envs = envs_from_tox_ini()
elif isinstance(envs, str):
envs = [envs]
default_indent = " " * spaces
if TOX4:
result = ""
else:
result = "___________________________________ summary ____________________________________\n"
for i, env in enumerate(envs):
if TOX4:
# Skip indentation for the first line
indent = default_indent if i > 0 else ""
result += f"{indent} {env}: OK\n"
else:
result += f"{default_indent} {env}: commands succeeded\n"
result += f"{default_indent} congratulations :)"
return result
def prep_tox_output(output):
"""Remove time info from tox output"""
result = re.sub(r" \((\d+\.\d+|\d+) seconds\)", "", output)
result = re.sub(r" ✔ in (\d+\.\d+|\d+) seconds", "", result)
return result
needs_all_pythons = pytest.mark.skipif(
not all((is_available(f"python3.{x}") for x in range(6, 12))),
reason="This test needs all pythons from 3.6 to 3.11 available in $PATH",
)
|