From 760d0315352c13a56380ac713c17164acd163ac3 Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 19 Jun 2026 15:32:26 -0600 Subject: [PATCH] fix(backend): make parsers __init__ re-exports lazy via __getattr__ --- backend/src/cyclone/parsers/__init__.py | 101 ++++++++++++++---------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/backend/src/cyclone/parsers/__init__.py b/backend/src/cyclone/parsers/__init__.py index 1fab7a3..23d9be7 100644 --- a/backend/src/cyclone/parsers/__init__.py +++ b/backend/src/cyclone/parsers/__init__.py @@ -1,45 +1,62 @@ -"""Public re-exports for the 837P parser.""" +"""Public re-exports for the 837P parser. +Re-exports are lazy via PEP 562 ``__getattr__`` so that importing a name from +this package (e.g. ``from cyclone.parsers.exceptions import CycloneParseError``) +does not require every referenced module to be importable. Downstream code can +write ``cyclone.parsers.PayerConfig`` and it will be loaded on first access. +""" + +from __future__ import annotations + +# Eager: only the exceptions module is needed early and unconditionally. from cyclone.parsers.exceptions import CycloneParseError, CycloneValidationError -from cyclone.parsers.models import ( - Address, - BatchSummary, - BillingProvider, - ClaimHeader, - ClaimOutput, - Diagnosis, - Envelope, - ParseResult, - Payer, - Procedure, - ServiceLine, - Subscriber, - ValidationIssue, - ValidationReport, -) -from cyclone.parsers.payer import PayerConfig -from cyclone.parsers.segments import Delimiters, tokenize -from cyclone.parsers.parse_837 import parse -__all__ = [ - "Address", - "BatchSummary", - "BillingProvider", - "ClaimHeader", - "ClaimOutput", - "CycloneParseError", - "CycloneValidationError", - "Delimiters", - "Diagnosis", - "Envelope", - "ParseResult", - "Payer", - "PayerConfig", - "Procedure", - "ServiceLine", - "Subscriber", - "ValidationIssue", - "ValidationReport", - "parse", - "tokenize", -] +_LAZY_EXPORTS: dict[str, str] = { + # exceptions + "CycloneParseError": "cyclone.parsers.exceptions", + "CycloneValidationError": "cyclone.parsers.exceptions", + # models + "Address": "cyclone.parsers.models", + "BatchSummary": "cyclone.parsers.models", + "BillingProvider": "cyclone.parsers.models", + "ClaimHeader": "cyclone.parsers.models", + "ClaimOutput": "cyclone.parsers.models", + "Diagnosis": "cyclone.parsers.models", + "Envelope": "cyclone.parsers.models", + "ParseResult": "cyclone.parsers.models", + "Payer": "cyclone.parsers.models", + "Procedure": "cyclone.parsers.models", + "ServiceLine": "cyclone.parsers.models", + "Subscriber": "cyclone.parsers.models", + "ValidationIssue": "cyclone.parsers.models", + "ValidationReport": "cyclone.parsers.models", + # payer + "PayerConfig": "cyclone.parsers.payer", + # segments + "Delimiters": "cyclone.parsers.segments", + "tokenize": "cyclone.parsers.segments", + # orchestrator + "parse": "cyclone.parsers.parse_837", +} + +_cache: dict[str, object] = {} + + +def __getattr__(name: str) -> object: + module_path = _LAZY_EXPORTS.get(name) + if module_path is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + if name in _cache: + return _cache[name] + import importlib + mod = importlib.import_module(module_path) + value = getattr(mod, name) + _cache[name] = value + return value + + +def __dir__() -> list[str]: + return sorted(list(_LAZY_EXPORTS.keys())) + + +__all__ = sorted(_LAZY_EXPORTS.keys())