Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion fluid_build/providers/odps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions fluid_build/providers/odps_standard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
24 changes: 24 additions & 0 deletions tests/providers/test_no_exporter_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)


Expand Down Expand Up @@ -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"
)
Loading