Skip to content

Migration

→ v1.13

v1.13 unifies the observation API onto one surface: dataset() is the composed training table, domain tables (obs(), the new climate(), forecasts(), satellite(), cwop.*) are the raw source-identified ingredients, and the numbered-mode era starts its deprecation train. Every existing entry point is byte-stable at runtime this release — nothing you call today returns different data.

The kwarg contract behind all of this lives in Source identity.

SurfaceBeforev1.13
Primary composerresearch(...)dataset(...)research() is a fully working alias bound to the same callable (dataset is research)
Per-report observationsresearch_by_source(source, ...) (mostlyright.mode2)obs(source=..., granularity="observation")
Standalone climate labelsonly inside the dataset() joinclimate(station, start, end) — a first-class domain table
Output shape knobas_dataframe=True/Falsereturn_type="dataframe" | "list" | "wrapper"
assert_source_identity homemostlyright.mode2mostlyright.core (re-exported)
Covariate columnsmanual hand-joindataset(include_satellite=, include_cwop=) (labels byte-identical, defaults off)

dataset() is the new canonical name for the leakage-safe training join. research() stays as a fully working alias — same callable, byte-identical output, no deprecation warning this release.

import mostlyright
# New canonical name
df = mostlyright.dataset("KNYC", "2025-01-06", "2025-01-12")
# research() is the same callable — no warning this release
same = mostlyright.research("KNYC", "2025-01-06", "2025-01-12")
assert mostlyright.dataset is mostlyright.research

Start using dataset() in new code. research() keeps working — it gains its own DeprecationWarning in a later release (1.14.x+) and is removed at 2.0.

The deprecation message on research_by_source() points at the new pinned path:

# old (deprecated, still works, still byte-identical to 1.12.x)
from mostlyright.mode2 import research_by_source
rows = research_by_source("iem.archive", "KNYC", "2025-01-06", "2025-01-12")
# new
from mostlyright.weather import obs
rows = obs("KNYC", "2025-01-06", "2025-01-12",
source="iem.archive", granularity="observation")

These two calls do NOT return the same row set, by design:

  • research_by_source() filters post-merge: the AWC > IEM > GHCNh fusion runs first, then rows from your pinned source are selected from the fused result. Rows your source reported but the merge superseded (because a higher-priority source covered the same timestamp) are absent.
  • obs(source=..., granularity="observation") fetches pre-merge via the source-isolated exact-window path: you get your source’s full coverage for the window — typically more rows, and a different per-day composition.

A feature pipeline built on the old post-merge rows and re-pointed at the new pre-merge rows will produce different aggregates, different distributions, and therefore a different model. Do not swap the call in a production strategy and keep trading. Treat the swap like a data-source change:

  1. Rebuild the training frame on the new path.
  2. Retrain.
  3. Re-backtest against settlements.
  4. Only then promote.

If you are not ready to retrain, do nothing: research_by_source() keeps its exact post-merge behavior for the whole deprecation window (≥2 minor releases). The warning is a schedule, not a behavior change.

The same logic applies to the new covariate flags: adding include_satellite=True / include_cwop=True to dataset() never changes label columns (byte-identical on vs off), but a model that starts consuming sat_*/cwop_* columns is a new model. Retrain deliberately.

Byte-stable at runtime — no action, no warning

Section titled “Byte-stable at runtime — no action, no warning”
SurfaceStatus in v1.13
dataset() / research() default pathIdentical output; dataset is research (same callable). research() emits no warning this release.
research_by_source(...) behaviorOutput byte-identical to 1.12.x (keeps its post-merge path). Only a warning is added.
obs(...) daily defaultgranularity="daily" is the Python default; no-argument calls unchanged.
live.latest / live.stream, snapshotUntouched.
Merge semantics, cache layout, CWOP firewallFrozen (parity gate green; the four firewall files are unchanged).
Default no-argument returns of every functionPreserved verbatim.

Warns (DeprecationWarning), behaves identically — migrate within ≥2 minors

Section titled “Warns (DeprecationWarning), behaves identically — migrate within ≥2 minors”
DeprecatedReplacementNotes
research_by_source(...) (call-time warning)obs(source=..., granularity="observation")Retrain event — see above.
import mostlyright.mode2 (import-path warning, once per session)obs(...) from mostlyright.weather; assert_source_identity from mostlyright.coreassert_source_identity’s canonical home is now mostlyright.core (same object, re-exported).
as_dataframe= on dataset()/research(), research_by_source(), obs()return_type= (True"dataframe", False"list")An explicit return_type= wins when both are passed (still warns).
SurfaceWhat it is
dataset()Canonical name for the training join. Start using it in new code.
obs(granularity="observation")Per-report merged rows, window-trimmed, observation_type + raw_metar surfaced; unpinned frames tagged merged.live_v1 (only schema.observation.merged.v1 accepts them).
climate(station, start, end)Standalone NWS CLI settlement-label table (JOIN vocabulary: cli_high_f/cli_low_f/cli_report_type), frame tag cli.archive, per-row source iem.
dataset(include_satellite=, include_cwop=)Labels-only covariate columns (sat_*, cwop_*), LEFT-joined per LST settlement day; needs the [satellite]/[cwop] extras; missing extras fail loud.
backend= + return_type= everywhereThe one output convention going forward.

If your pipeline promotes warnings to errors (python -W error::DeprecationWarning, filterwarnings = ["error"] in pytest, etc.), v1.13 will surface the new DeprecationWarnings as failures at:

  • any research_by_source() call,
  • any mostlyright.mode2 attribute access (once per session),
  • any explicit as_dataframe= argument.

Either migrate those call sites first, or add a targeted allowlist while you complete the migration window:

import warnings
warnings.filterwarnings(
"default",
category=DeprecationWarning,
module=r"mostlyright(\..*)?",
)

research() itself emits nothing this release, so the most common production entry point is unaffected.

  • dataset alias: import { dataset } from "mostlyright"dataset === research, same callable, no warning on research.
  • obs() granularity default DIVERGES from Python (deliberate): TS obs() defaults to granularity: "observation" — the per-report rows TS has always returned — so existing TS callers stay byte-stable. granularity: "daily" is not yet ported in TS and throws a documented error (parity ticket filed); it never silently returns per-report rows. Python’s default remains "daily". If you run both SDKs, pass granularity explicitly at the seam.
  • climate() lives at a subpath: import { climate } from "@mostlyrightmd/weather/climate" (not the root barrel — browser bundle-budget adaptation). Output matches Python’s column contract; station codes resolve NWS↔ICAO identically.
  • MODE2_* renamed with aliases: SOURCE_PINNED_SOURCES / SourcePinnedSource / isSourcePinnedSource are canonical; MODE2_SOURCES / Mode2Source / isMode2Source remain as deprecated aliases binding the same values (removal at 2.0).
  • return_type: "dataframe" is canonical; "frame" stays as a back-compat alias.
ReleaseWhat happens
v1.13 (this release)Warnings added; zero behavior change.
1.14.x+research() gains its DeprecationWarning (alias keeps working).
≥2 minors outEarliest removal window for research_by_source, mostlyright.mode2, as_dataframe.
2.0Removals land; TS MODE2_* aliases removed.
  • Source identity — the cross-vertical source= / delivery= / grain contract, frame-vs-row identity, and the labels-only covariate firewall.
  • Research API — the dataset() surface in full.
  • Changelog — the complete change list for v1.13.