From 4bfc3d24cc49650d6ef691937e887f2d805e625a Mon Sep 17 00:00:00 2001 From: hallerite Date: Mon, 25 May 2026 23:44:00 +0000 Subject: [PATCH 1/3] Lazy-load concrete renderer classes via PEP 562 __getattr__ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``renderers/__init__.py`` previously imported every concrete renderer class (``DefaultRenderer``, ``Qwen3Renderer``, …) at module load. Each of those modules imports ``transformers.tokenization_utils.PreTrainedTokenizer`` at the top, so plain ``import renderers`` always pulled ``transformers`` into ``sys.modules``. That blocked light-import callers — most visibly ``prime-rl-configs``'s slim-install path, which only needs the ``RendererConfig`` discriminated union from ``renderers.configs`` (no heavy deps). Defer the concrete-class imports to a module-level ``__getattr__`` (PEP 562). Configs and base symbols stay eager; ``from renderers import DefaultRenderer`` still works but only loads ``renderers.default`` (and hence ``transformers``) on first access. ``create_renderer`` is unaffected — ``renderers.base._populate_registry`` already lazy-imports the concrete classes when a renderer is instantiated. End-user API is unchanged. Full test suite passes. Co-Authored-By: Claude Opus 4.7 (1M context) --- renderers/__init__.py | 58 ++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/renderers/__init__.py b/renderers/__init__.py index 0ae78ac..a3a5546 100644 --- a/renderers/__init__.py +++ b/renderers/__init__.py @@ -59,20 +59,50 @@ Qwen3VLRendererConfig, RendererConfig, ) -from renderers.deepseek_v3 import DeepSeekV3Renderer -from renderers.default import DefaultRenderer -from renderers.glm5 import GLM5Renderer, GLM51Renderer -from renderers.glm45 import GLM45Renderer -from renderers.gpt_oss import GptOssRenderer -from renderers.kimi_k2 import KimiK2Renderer -from renderers.kimi_k25 import KimiK25Renderer -from renderers.laguna_xs2 import LagunaXS2Renderer -from renderers.minimax_m2 import MiniMaxM2Renderer -from renderers.nemotron3 import Nemotron3Renderer -from renderers.qwen3 import Qwen3Renderer -from renderers.qwen3_vl import Qwen3VLRenderer -from renderers.qwen35 import Qwen35Renderer -from renderers.qwen36 import Qwen36Renderer +# Concrete renderer classes are lazy-loaded so that consumers needing +# only the config layer (``RendererConfig`` discriminated union) — e.g. +# the slim ``prime-rl-configs`` install path — don't pay the +# ``transformers`` import cost. Each renderer module does ``from +# transformers.tokenization_utils import PreTrainedTokenizer`` at module +# level, so eager imports here would drag ``transformers`` into every +# downstream ``import renderers``. ``__getattr__`` (PEP 562) resolves +# the names on first attribute access, so ``from renderers import +# DefaultRenderer`` and ``renderers.DefaultRenderer`` both work +# transparently. ``create_renderer`` doesn't depend on these eager +# imports — ``renderers.base._populate_registry`` lazy-imports the +# concrete classes itself when a renderer is instantiated. +_LAZY_RENDERERS: dict[str, str] = { + "DeepSeekV3Renderer": "renderers.deepseek_v3", + "DefaultRenderer": "renderers.default", + "GLM45Renderer": "renderers.glm45", + "GLM51Renderer": "renderers.glm5", + "GLM5Renderer": "renderers.glm5", + "GptOssRenderer": "renderers.gpt_oss", + "KimiK25Renderer": "renderers.kimi_k25", + "KimiK2Renderer": "renderers.kimi_k2", + "LagunaXS2Renderer": "renderers.laguna_xs2", + "MiniMaxM2Renderer": "renderers.minimax_m2", + "Nemotron3Renderer": "renderers.nemotron3", + "Qwen35Renderer": "renderers.qwen35", + "Qwen36Renderer": "renderers.qwen36", + "Qwen3Renderer": "renderers.qwen3", + "Qwen3VLRenderer": "renderers.qwen3_vl", +} + + +def __getattr__(name: str): + if name in _LAZY_RENDERERS: + import importlib + + module = importlib.import_module(_LAZY_RENDERERS[name]) + value = getattr(module, name) + globals()[name] = value # cache for subsequent lookups + return value + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def __dir__() -> list[str]: + return sorted(set(globals().keys()) | set(_LAZY_RENDERERS)) __all__ = [ "AutoRendererConfig", From 8f6650dd7b922060acd76587046a41091ed1c9be Mon Sep 17 00:00:00 2001 From: hallerite Date: Mon, 25 May 2026 23:45:00 +0000 Subject: [PATCH 2/3] Apply ruff format Co-Authored-By: Claude Opus 4.7 (1M context) --- renderers/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/renderers/__init__.py b/renderers/__init__.py index a3a5546..ef72222 100644 --- a/renderers/__init__.py +++ b/renderers/__init__.py @@ -59,6 +59,7 @@ Qwen3VLRendererConfig, RendererConfig, ) + # Concrete renderer classes are lazy-loaded so that consumers needing # only the config layer (``RendererConfig`` discriminated union) — e.g. # the slim ``prime-rl-configs`` install path — don't pay the @@ -104,6 +105,7 @@ def __getattr__(name: str): def __dir__() -> list[str]: return sorted(set(globals().keys()) | set(_LAZY_RENDERERS)) + __all__ = [ "AutoRendererConfig", "BaseRendererConfig", From dc43e11216df5a125e3c9d8c39c64eb401e3518f Mon Sep 17 00:00:00 2001 From: hallerite Date: Mon, 25 May 2026 23:46:23 +0000 Subject: [PATCH 3/3] Drop prime-rl-configs reference from lazy-import comment The lazy-loading is a general win for any consumer of the config layer; no need to name a specific downstream. Co-Authored-By: Claude Opus 4.7 (1M context) --- renderers/__init__.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/renderers/__init__.py b/renderers/__init__.py index ef72222..c95719b 100644 --- a/renderers/__init__.py +++ b/renderers/__init__.py @@ -61,14 +61,13 @@ ) # Concrete renderer classes are lazy-loaded so that consumers needing -# only the config layer (``RendererConfig`` discriminated union) — e.g. -# the slim ``prime-rl-configs`` install path — don't pay the -# ``transformers`` import cost. Each renderer module does ``from -# transformers.tokenization_utils import PreTrainedTokenizer`` at module -# level, so eager imports here would drag ``transformers`` into every -# downstream ``import renderers``. ``__getattr__`` (PEP 562) resolves -# the names on first attribute access, so ``from renderers import -# DefaultRenderer`` and ``renderers.DefaultRenderer`` both work +# only the config layer (``RendererConfig`` discriminated union) don't +# pay the ``transformers`` import cost. Each renderer module does +# ``from transformers.tokenization_utils import PreTrainedTokenizer`` +# at module level, so eager imports here would drag ``transformers`` +# into every downstream ``import renderers``. ``__getattr__`` (PEP 562) +# resolves the names on first attribute access, so ``from renderers +# import DefaultRenderer`` and ``renderers.DefaultRenderer`` both work # transparently. ``create_renderer`` doesn't depend on these eager # imports — ``renderers.base._populate_registry`` lazy-imports the # concrete classes itself when a renderer is instantiated.