"""验证"插件是可选依赖"这一契约。 这个模块在**有/没有** FastAPI 的两种环境下都会运行: 它断言的是"核心包永远不依赖第三方框架",以及"缺依赖时的报错要能直接照做"。 """ import os import subprocess import sys import unittest import helpers THIRD_PARTY = ("fastapi", "starlette", "anyio", "pydantic") class OptionalDependencyTest(unittest.TestCase): def run_snippet(self, code): env = dict(os.environ) env["PYTHONPATH"] = helpers.SRC_DIR + os.pathsep + env.get("PYTHONPATH", "") env["PYTHONIOENCODING"] = "utf-8" return subprocess.run( [sys.executable, "-c", code], capture_output=True, text=True, encoding="utf-8", errors="replace", env=env, ) def test_third_party_is_installed_or_not(self): """只用于在输出里说明当前环境状态,不断言。""" result = self.run_snippet( "import importlib.util as u;" "print({m: bool(u.find_spec(m)) for m in %r})" % (THIRD_PARTY,) ) self.assertEqual(0, result.returncode, result.stderr) def test_importing_core_never_pulls_in_third_party(self): """import sqlite_vfs 不能顺带把 fastapi/starlette 拖进来。""" result = self.run_snippet( "import sys\n" "import sqlite_vfs, sqlite_vfs.core, sqlite_vfs.contrib\n" "leaked = sorted(\n" " m for m in sys.modules if m.split('.')[0] in %r\n" ")\n" "print(leaked)" % (THIRD_PARTY,) ) self.assertEqual(0, result.returncode, result.stderr) self.assertEqual( "[]", result.stdout.strip(), "导入 sqlite_vfs 时连带加载了第三方框架模块", ) def test_plugin_module_is_not_imported_by_the_package(self): """contrib 子包不能自动导入插件模块(否则缺依赖时整包不可用)。""" result = self.run_snippet( "import sys\n" "import sqlite_vfs.contrib\n" "print('sqlite_vfs.contrib.fastapi_static' in sys.modules)" ) self.assertEqual(0, result.returncode, result.stderr) self.assertEqual("False", result.stdout.strip()) def test_missing_fastapi_gives_actionable_error(self): """缺少可选依赖时,报错信息必须能直接照着做。""" probe = self.run_snippet( "import importlib.util as u; print(bool(u.find_spec('starlette')))" ) if probe.stdout.strip() == "True": self.skipTest("当前环境已安装 starlette,无需检查缺失提示") result = self.run_snippet("import sqlite_vfs.contrib.fastapi_static") self.assertNotEqual(0, result.returncode) self.assertIn("sqlite-vfs[fastapi]", result.stderr) self.assertIn("pip install", result.stderr) def test_missing_database_path_hint(self): """未生成资源包时的报错要给出下一步命令。""" result = self.run_snippet( "import tempfile, os, sys\n" "sys.path.insert(0, %r)\n" "try:\n" " from sqlite_vfs.contrib.fastapi_static import SVFSStaticFiles\n" "except ImportError as e:\n" " print('SKIP', e); raise SystemExit(0)\n" "try:\n" " SVFSStaticFiles(os.path.join(tempfile.mkdtemp(), 'nope.svfs'))\n" "except RuntimeError as e:\n" " print(e)\n" % helpers.SRC_DIR ) self.assertEqual(0, result.returncode, result.stderr) output = result.stdout.strip() if output.startswith("SKIP"): self.skipTest("当前环境没有 FastAPI") self.assertIn("vfspack", output) if __name__ == "__main__": unittest.main()