From 7a072c219f09f3df2886174fe56fb866a42f3913 Mon Sep 17 00:00:00 2001 From: Speculator55005 <50082482+fas89@users.noreply.github.com> Date: Sat, 27 Jun 2026 16:16:45 +0200 Subject: [PATCH] fix(providers): make exporter de-registration explicit + the invariant robust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Final-verdict polish on the provider-vs-exporter taxonomy (#300): - odps_standard/__init__ escaped auto-registration only BY ACCIDENT (it exposes two BaseProvider subclasses, so the single-subclass fallback skips it). Set __fluid_no_autoregister__ = True explicitly, matching odcs; also set it on odps/__init__ (belt-and-suspenders even though it exposes no subclass) so all three exporter packages opt out the same, robust way. - test_no_exporter_providers.py: broaden the non-deploying-apply markers to also catch a refusal via NotImplementedError / 'not implemented' (so a future exporter can't slip past with different wording), and add a consistency guard asserting every exporter package sets __fluid_no_autoregister__ — VERIFIED it fails if the flag is removed. No behaviour change: live registry still = aws/datamesh_manager/gcp/local/ redshift/snowflake (no exporter), all exporters still importable. 29 passed; ruff + black==24.10.0 clean. --- fluid_build/providers/odps/__init__.py | 5 +++- .../providers/odps_standard/__init__.py | 6 +++++ tests/providers/test_no_exporter_providers.py | 24 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/fluid_build/providers/odps/__init__.py b/fluid_build/providers/odps/__init__.py index 631621b0..d06c19bb 100644 --- a/fluid_build/providers/odps/__init__.py +++ b/fluid_build/providers/odps/__init__.py @@ -24,4 +24,7 @@ # DIRECTLY: from fluid_build.providers.odps.odps import OdpsProvider # (see cli/odps.py, cli/export_odps.py, cli/generate_standard.py). This package # __init__ deliberately exposes no BaseProvider subclass, so the provider -# auto-discovery scan cannot re-register it. +# auto-discovery scan cannot re-register it — and we ALSO set the explicit +# opt-out below (consistent with the odcs / odps_standard sibling packages) so a +# future refactor that re-exposed the class here can't silently re-register it. +__fluid_no_autoregister__ = True diff --git a/fluid_build/providers/odps_standard/__init__.py b/fluid_build/providers/odps_standard/__init__.py index 3131c7c6..6d9512eb 100644 --- a/fluid_build/providers/odps_standard/__init__.py +++ b/fluid_build/providers/odps_standard/__init__.py @@ -25,5 +25,11 @@ # them DIRECTLY: from fluid_build.providers.odps_standard import ( # BitolOdpsProvider, OdpsStandardProvider) # (see cli/odps.py, cli/odps_standard.py, cli/generate_standard.py). +# +# Set the opt-out EXPLICITLY (like the odps/odcs sibling packages) rather than +# relying on the accident that this module exposes two BaseProvider subclasses +# and so escapes the single-subclass auto-register fallback. Otherwise a future +# refactor that left a single exporter class here would silently re-register it. +__fluid_no_autoregister__ = True __all__ = ["BitolOdpsProvider", "OdpsStandardProvider"] diff --git a/tests/providers/test_no_exporter_providers.py b/tests/providers/test_no_exporter_providers.py index be9b5e8f..232b9988 100644 --- a/tests/providers/test_no_exporter_providers.py +++ b/tests/providers/test_no_exporter_providers.py @@ -31,6 +31,11 @@ "does not support apply", "export action acknowledged", "use render() for actual export", + # An apply() whose body is just a refusal is an exporter, not a provider — + # cover the common refusal shapes so a future sibling can't slip past with + # different wording. + "notimplementederror", + "not implemented", ) @@ -70,3 +75,22 @@ def test_odcs_exporter_still_importable_directly(): from fluid_build.providers.odcs import OdcsProvider assert hasattr(OdcsProvider(), "render") + + +def test_exporter_packages_explicitly_opt_out_of_autoregistration(): + # The exporter packages must de-register EXPLICITLY (set + # __fluid_no_autoregister__), not rely on incidental properties of their + # module namespace (e.g. exposing >1 BaseProvider subclass). Otherwise a + # refactor could silently re-register them as providers. + import importlib + + for modname in ( + "fluid_build.providers.odps", + "fluid_build.providers.odcs", + "fluid_build.providers.odps_standard", + ): + mod = importlib.import_module(modname) + assert getattr(mod, "__fluid_no_autoregister__", False) is True, ( + f"{modname} must set __fluid_no_autoregister__ = True (it is an exporter, " + "not a provider) instead of relying on auto-discovery accidents" + )