The tracked files still pointed at the development documents, which now live in the untracked .dev tree: the usage headings promised the embedded interpreter while the examples are interpreter agnostic, and the provenance notes named a local document. The headings are reworded and the provenance notes name the tool that produces the record instead.
92 lines
2.8 KiB
Python
92 lines
2.8 KiB
Python
"""P1.5 - simulate the CI environment and run the gate in it.
|
|
|
|
The CI job installs a **fixed** list of packages; everything else that happens
|
|
to be installed on a developer machine must not be needed by the suite. A
|
|
package that the suite needs but the CI install list omits turns the CI job
|
|
red on its first run (this is how PyYAML was missed: the vendored tree imports
|
|
``yaml`` eagerly at ``modules/deps/basicsr/utils/options.py:5``).
|
|
|
|
This tool reproduces the CI environment - it blocks the packages the CI
|
|
install list does **not** contain - and runs the gate command of the workflow
|
|
in it. The block list is the "dev machine only" set of the P1 audit; add more
|
|
with ``--block``.
|
|
|
|
Usage (embedded python, from the repository root):
|
|
|
|
python tools/ci_env_sim.py
|
|
python tools/ci_env_sim.py --block yaml
|
|
|
|
Exit code: the pytest exit code of the gate run.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from importlib.abc import MetaPathFinder
|
|
|
|
#: The gate command of ``.github/workflows/tests.yml`` (and of the plan).
|
|
GATE = "-m"
|
|
GATE_MARKERS = "unit or mock_integration or headless"
|
|
|
|
#: Packages that are installed on a development machine but **not** by the CI
|
|
#: install step (``.github/workflows/tests.yml``). The suite must not need
|
|
#: them - if it does, the CI install list is incomplete.
|
|
DEV_ONLY_PACKAGES = (
|
|
"addict",
|
|
"cupy",
|
|
"diffusers",
|
|
"ffmpeg",
|
|
"future",
|
|
"lmdb",
|
|
"mmcv",
|
|
"skimage",
|
|
"yapf",
|
|
)
|
|
|
|
|
|
class _BlockingFinder(MetaPathFinder):
|
|
"""Makes the blocked top-level modules unimportable."""
|
|
|
|
def __init__(self, names: tuple[str, ...]) -> None:
|
|
self.names = frozenset(names)
|
|
|
|
def find_spec(self, fullname, path=None, target=None): # noqa: ANN001, D102
|
|
if fullname.split(".")[0] in self.names:
|
|
raise ModuleNotFoundError(f"No module named {fullname!r}", name=fullname)
|
|
return None
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--block",
|
|
action="append",
|
|
default=[],
|
|
metavar="MODULE",
|
|
help="an additional top-level module to block (repeatable)",
|
|
)
|
|
parser.add_argument(
|
|
"--list-blocked",
|
|
action="store_true",
|
|
help="print the blocked modules and exit",
|
|
)
|
|
args = parser.parse_args(argv)
|
|
|
|
blocked = DEV_ONLY_PACKAGES + tuple(args.block)
|
|
if args.list_blocked:
|
|
for name in blocked:
|
|
print(name)
|
|
return 0
|
|
|
|
sys.meta_path.insert(0, _BlockingFinder(blocked))
|
|
print(f"[ci-env-sim] blocked modules: {', '.join(blocked)}")
|
|
|
|
import pytest # noqa: PLC0415 - imported after the blocker is installed
|
|
|
|
return pytest.main(["-q", GATE, GATE_MARKERS])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|