Skip to content

Replace silent table=None fallback in calibration/solver_common.py with explicit ImportError #79

Description

@jakobtfaber

Context

dsa110_continuum/calibration/solver_common.py (lines 23-29) silently sets table = None when dsa110_continuum.adapters.casa_tables cannot be imported:

try:
    from dsa110_continuum.adapters import casa_tables as _casatables  # type: ignore
    table = _casatables.table  # noqa: N816
except ImportError:
    _casatables = None
    table = None

This pattern was carried over verbatim from the legacy 3,000-line calibration.py during the May 2026 solver split (solve_delay.py / solve_bandpass.py / solve_gains.py / solver_common.py).

Problem

Downstream callers do with table(...) as t: without checking the sentinel. When the adapter is unavailable (e.g., a cloud VM with no CASA stack), the failure surfaces as TypeError: 'NoneType' object is not callable deep inside a solver — confusing and far from the root cause.

Only solve_gains.solve_gains (around line 418) currently does the right thing:

_table = sys.modules[__name__].table
if _table is None:
    raise ImportError(
        "dsa110_continuum.adapters.casa_tables module is not available. ..."
    )

The other consumers (solve_bandpass, solve_delay, and indirect users via the calibration.py re-export shim) do not.

Why it's currently preserved

The fallback intentionally matches legacy behavior so the import chain does not blow up on environments without CASA. Per AGENTS.md, dsa110_continuum/ is still bridged to the legacy dsa110_contimg package, and a hard import error at module load would cascade.

Proposal (when legacy bridge is gone)

Replace the silent fallback with one of:

  1. Re-raise the ImportError at first use (lazy guard wrapper around table), with a clear message naming the missing dependency and the casa6 conda env.
  2. Keep top-level import permissive but uniformly check _table is None in every consumer site (apply the solve_gains pattern in solve_bandpass and solve_delay).

Option 2 is the safer interim step; option 1 is the end state once the legacy bridge is removed.

Acceptance criteria

  • No with table(...) call site raises TypeError: 'NoneType' is not callable.
  • Missing adapter raises a single, clear ImportError mentioning dsa110_continuum.adapters.casa_tables and the casa6 env requirement.
  • Legacy dsa110_contimg shim still imports without CASA installed (verify on a clean cloud VM).
  • Tests in tests/test_field_directions.py and tests/test_calibration_flag_fraction.py still pass.

References

  • Code: dsa110_continuum/calibration/solver_common.py:23-29
  • Good-pattern reference: dsa110_continuum/calibration/solve_gains.py:417-423
  • Discussion: code review on May 2026 calibration solver split (working changes review).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestready-for-agentFully specified, ready for an AFK agent to pick up cold

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions