#!/usr/bin/env python3 """Cells for wizard-work/spark/python-env-check.py (queue Q120). The script under test is run as a subprocess, exactly as an operator runs it; nothing here imports it. The red cell picks its missing package by measuring the host first, so it is a real absence rather than a name the test hopes is absent. Run: python3 -m unittest discover -p 'test_python_env_check.py' -v (from wizard-work/spark, or by path from anywhere) """ import importlib.util import json import os import subprocess import sys import unittest HERE = os.path.dirname(os.path.abspath(__file__)) SCRIPT = os.path.join(HERE, "python-env-check.py") # Real, widely-installed packages the spark lane does not depend on. The red # cell requires whichever of these the host genuinely lacks. RED_CANDIDATES = ("flask", "django", "pandas", "scipy", "tensorflow", "torch", "black") def run_script(*args, cwd=None): proc = subprocess.run( [sys.executable, SCRIPT] + list(args), cwd=cwd or HERE, capture_output=True, text=True, ) return proc def absent_package(): """Return the first candidate module that cannot be found on this host.""" for name in RED_CANDIDATES: try: found = importlib.util.find_spec(name) except (ImportError, ValueError): found = None if found is None: return name return None class EnvCheckContract(unittest.TestCase): def setUp(self): self.proc = run_script() self.assertEqual( self.proc.returncode in (0, 1), True, "expected rc 0 or 1, got %r: %s" % (self.proc.returncode, self.proc.stderr), ) self.report = json.loads(self.proc.stdout) def test_declared_list_is_not_the_standard_library(self): # The defect Q120 filed: the only names checked were json/subprocess/ # datetime, so no healthy interpreter could ever fail this script. packages = self.report["components"]["packages"] self.assertTrue(packages, "no packages reported at all") stdlib = set(getattr(sys, "stdlib_module_names", ())) for import_name in packages: self.assertNotIn( import_name, stdlib, "%r is a standard-library module, not a lane dependency" % import_name, ) def test_declared_list_is_the_lane_dependencies(self): packages = self.report["components"]["packages"] self.assertEqual( set(packages), {"yaml", "numpy", "PIL", "jsonschema"} ) # email_validator is the lane's own stdlib-only module, not the PyPI # distribution; declaring it would test something the lane never uses. self.assertNotIn("email_validator", packages) def test_every_declared_dependency_reports_a_version(self): packages = self.report["components"]["packages"] for import_name, result in packages.items(): self.assertEqual(result["status"], "ok", "%s: %r" % (import_name, result)) self.assertIn("version", result) self.assertIn("distribution", result) self.assertFalse( result["version"] in ("", "unknown"), "%s reported no version: %r" % (import_name, result), ) def test_all_ok_agrees_with_the_missing_list(self): self.assertEqual(self.report["all_ok"], self.report["missing"] == []) self.assertEqual(self.proc.returncode, 0 if self.report["all_ok"] else 1) def test_genuinely_absent_package_turns_the_reading_red(self): name = absent_package() if name is None: self.skipTest( "every candidate %r is installed here; no real package to " "prove the red path with" % (RED_CANDIDATES,) ) proc = run_script("--require", name) self.assertEqual(proc.returncode, 1, proc.stdout + proc.stderr) report = json.loads(proc.stdout) self.assertFalse(report["all_ok"]) self.assertEqual(report["missing"], [name]) result = report["components"]["packages"][name] self.assertEqual(result["status"], "missing") self.assertIn(name, result["error"]) # The healthy declared dependencies still report alongside it. self.assertEqual(report["dependencies_declared"], 5) self.assertEqual( report["components"]["packages"]["yaml"]["status"], "ok" ) def test_usage_error_is_rc_two(self): proc = run_script("--require") self.assertEqual(proc.returncode, 2, proc.stderr) def test_same_reading_from_a_different_cwd(self): other = run_script(cwd="/") self.assertEqual(other.returncode, self.proc.returncode) self.assertEqual(json.loads(other.stdout)["missing"], self.report["missing"]) if __name__ == "__main__": unittest.main(verbosity=2)