Skip to content

disdantic.diagnose

Registry diagnostics and integrity check orchestrator for subclass registries.

This module provides programmatic tools and interfaces to scan Python packages, recursively import modules to trigger dynamic model registration, and verify the Pydantic compilation health and consistency of all registered classes.

Veteran maintainers can utilize this module's verify_registries function to run diagnostic checks, while new contributors can refer to it to verify that their newly created Pydantic subclasses are properly auto-discovered and compiled.

DiagnosticsReport

Bases: BaseModel

Aggregated health status and discovery details of all checked registries.

This class serves as the root container returned by the verification pipeline. It consolidates individual registry diagnostics, scanned packages, and import failure traces into a single report.

Examples:

.. code-block:: python

from disdantic.diagnose import verify_registries

report = verify_registries()
if not report.is_healthy:
    print(f"Scanned packages: {report.scanned_packages}")
    print(f"Import errors: {report.import_errors}")
Source code in src/disdantic/diagnose.py
class DiagnosticsReport(BaseModel):
    """Aggregated health status and discovery details of all checked registries.

    This class serves as the root container returned by the verification pipeline. It
    consolidates individual registry diagnostics, scanned packages, and import failure
    traces into a single report.

    Examples:
        .. code-block:: python

            from disdantic.diagnose import verify_registries

            report = verify_registries()
            if not report.is_healthy:
                print(f"Scanned packages: {report.scanned_packages}")
                print(f"Import errors: {report.import_errors}")
    """

    is_healthy: bool = Field(
        description=(
            "Indicates whether the entire check is clean, enabling safe dynamic model "
            "resolution with zero registry or import failures."
        )
    )
    scanned_packages: list[str] = Field(
        description="Maps the list of Python package names traversed during discovery."
    )
    registries: list[RegistryDiagnostics] = Field(
        default_factory=list,
        description=(
            "Configures the collection of registry-specific diagnostics reports."
        ),
    )
    import_errors: list[str] = Field(
        default_factory=list,
        description=(
            "Maps the traceback strings for import errors encountered during scanning."
        ),
    )

RegistryDiagnostics

Bases: BaseModel

Configuration, model metadata, and integrity status of a registry.

This class tracks the metadata of a single registry subclassing RegistryMixin. It records the dynamic auto-discovery configuration, registered sub-models, and any orphaned subclasses that were imported but failed to register.

Examples:

.. code-block:: python

from disdantic.diagnose import verify_registries

report = verify_registries()
for registry in report.registries:
    print(f"Registry: {registry.registry_name}")
    print(f"Orphaned subclasses: {registry.orphans}")
Source code in src/disdantic/diagnose.py
class RegistryDiagnostics(BaseModel):
    """Configuration, model metadata, and integrity status of a registry.

    This class tracks the metadata of a single registry subclassing RegistryMixin. It
    records the dynamic auto-discovery configuration, registered sub-models, and any
    orphaned subclasses that were imported but failed to register.

    Examples:
        .. code-block:: python

            from disdantic.diagnose import verify_registries

            report = verify_registries()
            for registry in report.registries:
                print(f"Registry: {registry.registry_name}")
                print(f"Orphaned subclasses: {registry.orphans}")
    """

    registry_name: str = Field(
        description="Maps the name of the registry base class under diagnostic check."
    )
    discriminator_key: str = Field(
        description=(
            "Configures the attribute name used to identify model types in polymorphic "
            "schemas."
        )
    )
    auto_discovery_enabled: bool = Field(
        description=(
            "Enables package scanning and dynamic registration for this registry."
        )
    )
    models: list[RegistryModelInfo] = Field(
        default_factory=list,
        description=(
            "Configures the list of registered models and their compilation states."
        ),
    )
    orphans: list[str] = Field(
        default_factory=list,
        description="Maps imported subclasses that are not registered with any key.",
    )

RegistryModelInfo

Bases: BaseModel

Metadata and compilation status of a registered subclass inside a registry.

This class captures the import details, registration key, and Pydantic schema compilation status of a single model registered under a RegistryMixin class.

Examples:

.. code-block:: python

from disdantic.diagnose import verify_registries

report = verify_registries()
for registry in report.registries:
    for model in registry.models:
        if model.compilation_status == "error":
            print(
                f"Model {model.class_name} compilation error: "
                f"{model.error_detail}"
            )
Source code in src/disdantic/diagnose.py
class RegistryModelInfo(BaseModel):
    """Metadata and compilation status of a registered subclass inside a registry.

    This class captures the import details, registration key, and Pydantic schema
    compilation status of a single model registered under a RegistryMixin class.

    Examples:
        .. code-block:: python

            from disdantic.diagnose import verify_registries

            report = verify_registries()
            for registry in report.registries:
                for model in registry.models:
                    if model.compilation_status == "error":
                        print(
                            f"Model {model.class_name} compilation error: "
                            f"{model.error_detail}"
                        )
    """

    key: str = Field(
        description="Maps the unique lookup key used to register this subclass."
    )
    class_name: str = Field(description="Maps the name of the registered Python class.")
    module_path: str = Field(
        description="Maps the import path of the module defining the model class."
    )
    compilation_status: str = Field(
        description="Configures the health status, set to 'healthy' or 'error'."
    )
    error_detail: str | None = Field(
        default=None,
        description=(
            "Maps the traceback or validation error message if compilation failed."
        ),
    )

verify_registries(settings=None)

Scan configured packages, discover registries, and verify compilation.

Examples:

.. code-block:: python

from disdantic.diagnose import verify_registries

report = verify_registries()
if not report.is_healthy:
    print(f"Diagnostics failed. Errors: {report.import_errors}")

:param settings: Optional settings configuration instance to customize packages and ignore rules. :returns: The aggregated health diagnostics report.

Source code in src/disdantic/diagnose.py
def verify_registries(settings: Settings | None = None) -> DiagnosticsReport:
    """Scan configured packages, discover registries, and verify compilation.

    Examples:
        .. code-block:: python

            from disdantic.diagnose import verify_registries

            report = verify_registries()
            if not report.is_healthy:
                print(f"Diagnostics failed. Errors: {report.import_errors}")

    :param settings: Optional settings configuration instance to customize packages
        and ignore rules.
    :returns: The aggregated health diagnostics report.
    """
    if settings is not None:
        with disdantic.settings._settings_lock:  # noqa: SLF001
            disdantic.settings._global_settings = settings  # noqa: SLF001

    resolved_settings = disdantic.settings.get_settings()
    unique_packages = _resolve_packages_to_scan(resolved_settings)
    ignore_set = _resolve_ignore_set(resolved_settings)

    import_errors: list[str] = []
    _scan_packages(unique_packages, ignore_set, import_errors)

    valid_registries = _find_valid_registries()

    registries_diagnostics: list[RegistryDiagnostics] = []
    is_healthy = len(import_errors) == 0

    for registry_class in valid_registries:
        registry_diag = _diagnose_registry(registry_class, import_errors)
        registries_diagnostics.append(registry_diag)

        if issubclass(registry_class, PydanticClassRegistryMixin):
            try:
                registry_class.model_rebuild(force=True, raise_errors=True)
                registry_class.model_json_schema()
            except Exception:  # noqa: BLE001
                is_healthy = False

        if any(model.compilation_status == "error" for model in registry_diag.models):
            is_healthy = False

    if import_errors:
        is_healthy = False

    return DiagnosticsReport(
        is_healthy=is_healthy,
        scanned_packages=unique_packages,
        registries=registries_diagnostics,
        import_errors=import_errors,
    )