|
| 1 | +"""Deprecation shims must re-export canonical symbols and warn on import. |
| 2 | +
|
| 3 | +Flat ``unplug.core.*`` modules (and a few package-root paths) exist until v1.0 |
| 4 | +so old imports keep working. See ``sdk/MIGRATION.md``. |
| 5 | +
|
| 6 | +Shims that still need DeprecationWarning (used internally today, so warning |
| 7 | +on import would fire for ``import unplug``): |
| 8 | +- ``unplug.core.models`` → ``unplug.ml.models`` |
| 9 | +- ``unplug.core.extras`` → ``unplug.optional._base`` |
| 10 | +""" |
| 11 | + |
| 12 | +from __future__ import annotations |
| 13 | + |
| 14 | +import importlib |
| 15 | +import sys |
| 16 | + |
| 17 | +import pytest |
| 18 | + |
| 19 | +# (shim_module, canonical_module, attr) |
| 20 | +# All of these emit DeprecationWarning on (re)import. |
| 21 | +_WARNING_SHIMS: list[tuple[str, str, str]] = [ |
| 22 | + ("unplug.core.approval", "unplug.core.agent.approval", "ApprovalProvider"), |
| 23 | + ("unplug.core.asyncio_compat", "unplug.core.runtime.asyncio_compat", "run_coroutine_sync"), |
| 24 | + ("unplug.core.boundaries", "unplug.core.agent.boundaries", "wrap_external_content"), |
| 25 | + ("unplug.core.cache", "unplug.core.runtime.cache", "ScanCache"), |
| 26 | + ("unplug.core.canary", "unplug.core.agent.canary", "CanaryRegistry"), |
| 27 | + ("unplug.core.collusion", "unplug.core.agent.collusion", "collusion_findings"), |
| 28 | + ("unplug.core.config", "unplug.config.guard", "PipelineConfig"), |
| 29 | + ("unplug.core.config_loader", "unplug.config.loader", "load_from_file"), |
| 30 | + ("unplug.core.content", "unplug.providers.content.protocol", "ContentProvider"), |
| 31 | + ("unplug.core.decision", "unplug.core.policy.decision", "should_invoke_ml"), |
| 32 | + ("unplug.core.degradation", "unplug.core.agent.degradation", "degraded_tool_findings"), |
| 33 | + ("unplug.core.disposition", "unplug.core.policy.disposition", "DispositionLabel"), |
| 34 | + ("unplug.core.encodings", "unplug.core.normalize.encodings", "HeuristicEncodingClassifier"), |
| 35 | + ("unplug.core.intent", "unplug.core.agent.intent", "check_intent_mismatch"), |
| 36 | + ("unplug.core.limits", "unplug.config.limits", "LimitConfig"), |
| 37 | + ("unplug.core.logging", "unplug.core.runtime.logging", "get_correlation_id"), |
| 38 | + ("unplug.core.luhn", "unplug.core.privacy.luhn", "luhn_valid"), |
| 39 | + ( |
| 40 | + "unplug.core.model_runtime", |
| 41 | + "unplug.core.runtime.model_runtime", |
| 42 | + "load_active_model_provider", |
| 43 | + ), |
| 44 | + ("unplug.core.secrets", "unplug.core.privacy.secrets", "SecretsRegistry"), |
| 45 | + ( |
| 46 | + "unplug.core.sensitive_context", |
| 47 | + "unplug.core.policy.sensitive_context", |
| 48 | + "apply_sensitive_context_boost", |
| 49 | + ), |
| 50 | + ("unplug.core.stats", "unplug.core.runtime.stats", "MetricsCollector"), |
| 51 | + ("unplug.core.toolchain", "unplug.core.agent.toolchain", "toolchain_findings"), |
| 52 | + ("unplug.core.trajectory", "unplug.core.agent.trajectory", "trajectory_findings"), |
| 53 | + ("unplug.core.versions", "unplug.core.runtime.versions", "NORMALIZER_VERSION"), |
| 54 | + ("unplug.guard_scan", "unplug.api.results", "refresh_scan_result"), |
| 55 | + ("unplug.scanner", "unplug.scanners.base", "Scanner"), |
| 56 | + ("unplug.safeguards", "unplug.scanners", "ScannerRegistry"), |
| 57 | +] |
| 58 | + |
| 59 | +# Re-export only (no DeprecationWarning yet — see module docstring). |
| 60 | +_SILENT_SHIMS: list[tuple[str, str, str]] = [ |
| 61 | + ("unplug.core.models", "unplug.ml.models", "ModelProvider"), |
| 62 | + ("unplug.core.extras", "unplug.optional._base", "require_extra"), |
| 63 | +] |
| 64 | + |
| 65 | + |
| 66 | +def _reload_fresh(module_name: str) -> object: |
| 67 | + """Drop cached module so the next import re-runs module-level warn().""" |
| 68 | + sys.modules.pop(module_name, None) |
| 69 | + return importlib.import_module(module_name) |
| 70 | + |
| 71 | + |
| 72 | +@pytest.mark.parametrize(("shim", "canonical", "attr"), _WARNING_SHIMS) |
| 73 | +def test_deprecation_shim_warns_and_reexports(shim: str, canonical: str, attr: str) -> None: |
| 74 | + canon_mod = importlib.import_module(canonical) |
| 75 | + expected = getattr(canon_mod, attr) |
| 76 | + |
| 77 | + with pytest.warns(DeprecationWarning, match="deprecated"): |
| 78 | + shim_mod = _reload_fresh(shim) |
| 79 | + |
| 80 | + assert getattr(shim_mod, attr) is expected |
| 81 | + |
| 82 | + |
| 83 | +@pytest.mark.parametrize(("shim", "canonical", "attr"), _SILENT_SHIMS) |
| 84 | +def test_silent_reexport_shim_identity(shim: str, canonical: str, attr: str) -> None: |
| 85 | + # Intentionally no DeprecationWarning yet (see module docstring). |
| 86 | + canon_mod = importlib.import_module(canonical) |
| 87 | + shim_mod = importlib.import_module(shim) |
| 88 | + assert getattr(shim_mod, attr) is getattr(canon_mod, attr) |
| 89 | + |
| 90 | + |
| 91 | +def test_safeguards_registry_alias_is_scanner_registry() -> None: |
| 92 | + with pytest.warns(DeprecationWarning, match="deprecated"): |
| 93 | + safeguards = _reload_fresh("unplug.safeguards") |
| 94 | + from unplug.scanners import ScannerRegistry |
| 95 | + |
| 96 | + assert safeguards.SafeguardRegistry is ScannerRegistry |
| 97 | + assert safeguards.ScannerRegistry is ScannerRegistry |
0 commit comments