Skip to content

mostlyright.core.result

MostlyRightResult — backend-neutral provenance wrapper.

Phase 6 (v0.2+) deliverable. Polars frames have no .attrs, so the v0.1.0 pattern of stamping df.attrs["source"] / df.attrs["retrieved_at"] cannot survive the polars backend. MostlyRightResult carries the provenance separately from the frame so both pandas and polars callers preserve source-identity invariants without .attrs writes.

Validator, KnowledgeView, and LeakageDetector accept either a raw DataFrame (legacy v0.1.0 path) OR a MostlyRightResult (new v0.2 path); the wrapper-aware dispatch unwraps via frame_as_pandas() and the existing validation logic runs unchanged.

See PLAN.md Wave 0 + CONTEXT.md “User Decisions” §2 and §7.

MostlyRightResult(frame, source, retrieved_at)Backend-neutral provenance wrapper for a DataFrame-returning call.

class mostlyright.core.result.MostlyRightResult(frame, source, retrieved_at, schema_id=None, qc=None, data_version=None)

Section titled “class mostlyright.core.result.MostlyRightResult(frame, source, retrieved_at, schema_id=None, qc=None, data_version=None)”

Bases: object

Backend-neutral provenance wrapper for a DataFrame-returning call.

Both pandas-backend and polars-backend adapters return the same wrapper shape. frame holds the native frame; the remaining fields carry the provenance that v0.1.0 used to stamp on df.attrs.

The underlying DataFrame (pandas OR polars). Optional polars type is type-hinted via TYPE_CHECKING so default install does not require polars.

The canonical source identifier (e.g. "iem.live", "awc.live", "noaa_bdp"). Mirrors the v0.1.0 df.attrs["source"] contract.

UTC timestamp of the fetch. MUST be tz-aware.

Optional canonical schema ID (e.g. "schema.observation.v1"). None if the call returns heterogeneous rows (e.g. research() pairs).

Optional QC summary (rules_fired counts, sidecar_paths) if the caller invoked qc=True. Mirrors df.attrs["qc"].

Optional DataVersion token for reproducibility.

>>> import pandas as pd
>>> from datetime import datetime, timezone
>>> from mostlyright.core.result import MostlyRightResult
>>> df = pd.DataFrame({"date": ["2025-01-01"], "value": [42]})
>>> result = MostlyRightResult(
... frame=df,
... source="iem.live",
... retrieved_at=datetime(2025, 1, 1, tzinfo=timezone.utc),
... )
>>> result.source
'iem.live'
>>> result.frame_as_pandas().iloc[0]["value"]
42

Return the underlying frame as a pandas DataFrame.

Pandas frames pass through unchanged. Polars frames are converted via pl.DataFrame.to_pandas(). Parity-locked modules call this before running their pandas-only pipelines (Phase 6 PLAN.md W4-T1).

Polars→pandas conversion may shift datetime resolution (us → ns on the v0.1.0 contract path) and may change nullable-int storage. Callers that need byte-equivalent round-trip across backends MUST consult the documented coercion rules in tests/fixtures/parity/coerce_pd3.py.

  • Return type: DataFrame

Return the wrapped pandas DataFrame with df.attrs populated.

Mirrors the v0.1.0 df.attrs-stamped shape that catalog adapters produced: attrs["source"], attrs["retrieved_at"] (ISO string), and attrs["qc"] / attrs["data_version"] when set. For callers that still consume df.attrs["source"] directly instead of the wrapper, this method bridges the legacy expectation for one release cycle (strict deprecation lands in v0.3).

  • Raises: TypeError – if the wrapped frame is polars — the legacy shape was pandas-only by definition.
  • Return type: DataFrame

JSON-safe dict representation for v0.2 MCP JSON-RPC serialization.

Excludes the frame body — callers that need to ship rows over MCP should serialize the frame via mostlyright.core.formats.* writers and attach the provenance via this method’s output.