Overview
What EarthSciIO does, where it sits in the EarthSci stack, and how to use it.
EarthSciIO is a cross-language (Python / Julia / Rust) data-provider library that fulfills the EarthSci data-loader contract: it resolves a loader’s URL at a time, downloads and caches the file in a shared content-addressed cache, and reads it into native-grid arrays the simulation consumes at its cadence.
It is the sanctioned impure I/O boundary of the EarthSci stack: it provides data, never a solver. Variable remapping stays in EarthSciAST and regridding in EarthSciDiscretizations; see the boundary description in the spec overview .
The contract is the language-neutral provider spec
: the
shared cache and manifest format
(key = sha256(resolved_url)), the three extensibility
registries
(transport, format, store), the
offline-mode
contract, and the
conformance
corpus format. Each language is
implemented idiomatically against it rather than as one core behind FFI, and is
extensible by construction: an S3 transport, an object-store backend or a Zarr
reader slots in without touching the Provider API.
For runnable examples in all three languages see Getting started ; for every operation see the API reference .
Python (earthsciio/)
The Python track ships the URL download + content-addressed cache behind the
ESS opener/fetcher seam (esio-9nb.2). It registers the active http/file
transports and the local store into the three registries, alongside the
s3/zarr cloud stubs.
from earthsciio import Cache
cache = Cache() # root = $EARTHSCIDATADIR
entry = cache.fetch(
"https://data.earthsci.dev/era5/2018/11/20181108.nc",
source_loader="era5",
mirrors=["https://mirror.example/era5/20181108.nc"], # tried in order
)
entry.path # local blob (fetched once, shared across processes + languages)
entry.status # "downloaded" | "hit" | "not_modified"
# Offline (cache-only, hermetic) — never touches the network:
hit = Cache(offline=True).fetch(...) # raises CacheMiss on a miss
fetch is content-addressed by sha256(resolved_url), validates via
checksum / conditional-GET / TTL, writes atomically under a per-blob flock
(safe for many processes on one /scratch.local cache), and records a sidecar
manifest. Auth is a pluggable realm → resolver seam
(StaticHeaderAuth.bearer(...) / .header(...)); credentials never reach the
manifest. Run the suite with pytest (fully offline/hermetic).
CDS / ERA5 (cds transport)
The active cds transport speaks the Copernicus Climate Data Store API v1 —
submit → poll → download — behind the same fetch seam (plain requests, no
cdsapi SDK). A cds:// URL encodes the dataset + request Dict as canonical
JSON, so an identical request shares a cache key and a repeat is a hit, not a
second CDS job. earthsciio.era5 builds that request from ERA5 loader fields
(variable list, pressure levels, area from the domain bbox, the month/day/hour
span), ported from EarthSciData.jl.
from earthsciio import Cache, cds_auth, era5
url = era5.era5_cds_url(
2018, 11, [8], ["temperature", "u_component_of_wind"], [1000, 850, 500],
era5.era5_area_from_bbox(-100.5, 39.0, -80.2, 45.7), # [N, W, S, E]
)
cache = Cache(auth={"cds": cds_auth()}) # key from ~/.cdsapirc / $CDSAPI_KEY
entry = cache.fetch(url, source_loader="era5", auth_realm="cds")
Auth is the PRIVATE-TOKEN header carried by the cds realm; the key is read
from $CDSAPI_KEY or ~/.cdsapirc and only the realm name reaches the
manifest. CI is fully offline against a mock CDS server. The live pull is a
manual smoke test (needs a real key + one-time ERA5 licence acceptance on the
CDS portal):
EARTHSCI_LIVE=1 CDSAPI_KEY=<key> pytest -q -k live_cds