Skip to content

Migration guide#

This guide helps downstream packages that depend on pyramids migrate across breaking changes. It is organized by subpackage, and under each subpackage by the release that introduced the changes.

isinstance(x, NetCDF) keeps working throughout, so most code needs no change. Everything below either emits a DeprecationWarning (discoverable with the command in the next section) or is a hard behavior change (called out explicitly).

Finding what affects you (one command)#

Run your own test suite against the new pyramids with deprecation warnings turned into errors — this surfaces every deprecated call site:

python -W error::DeprecationWarning -m pytest

Or, in code under test:

import warnings
warnings.simplefilter("error", DeprecationWarning)

This catches the deprecations; the hard behavior changes do not warn — search for them manually.

netcdf#

0.37.0#

Introduces the Container / Variable type split plus a wave of API consolidation. Each change below shows what changed and the before → after so you can update at a glance.

At a glance#

breaking* = only breaks exact-type checks; isinstance(x, NetCDF) still holds.

Change Kind What you do
read_file / get_variable return Container / Variable breaking* use isinstance(x, NetCDF)
subset() returns a NetCDF, not a Dataset breaking* nothing unless you used type(x) is Dataset
CFInfo is frozen breaking dataclasses.replace(cf, ...)
LabeledDataset.read_file(engine=...) validates engine breaking pass a valid engine name
NetCDF(gdal_dataset) direct construction deprecated use read_file / get_variable
get_variable_names() deprecated variable_names property
ColourOpts deprecated ColorOpts
MetaData / DimMetaData deprecated ClassicDimMetadata / ClassicDimensionInfo
kerchunk backend="kerchunk" deprecated backend="legacy"
_LabeledArray / _apply_unpack renamed LabeledArray / apply_unpack

Breaking changes (update required)#

1. Opening a store returns Container; extracting a variable returns Variable. NetCDF is now a base class with two concrete subclasses. Both still pass isinstance(x, NetCDF), so only exact-type checks break.

# Before — everything was a NetCDF
nc  = NetCDF.read_file("cube.nc")     # NetCDF
var = nc.get_variable("t")            # NetCDF
type(nc) is NetCDF                    # True

# After
nc  = NetCDF.read_file("cube.nc")     # Container
var = nc.get_variable("t")            # Variable
isinstance(nc, NetCDF)                # True   <- use this
type(nc) is NetCDF                    # False  <- this no longer holds

2. subset() returns a NetCDF (a Variable), not a plain Dataset.

# Before
ds = nc.subset("t", time=0)           # Dataset

# After
var = nc.subset("t", time=0)          # Variable (a NetCDF); read_array()/crop()/sel() all still work

3. CFInfo is immutable (frozen). In-place assignment now raises dataclasses.FrozenInstanceError.

# Before
cf.<field> = new_value                # mutated in place

# After
import dataclasses
cf = dataclasses.replace(cf, <field>=new_value)

4. LabeledDataset.read_file(engine=...) validates the engine. An unrecognised value now raises ValueError instead of being silently ignored.

# Before
ds = LabeledDataset.read_file(store, engine="zar")    # typo silently ignored

# After — valid: "zarr", "netcdf", "netcdf4", "hdf5", "h5netcdf", or None
ds = LabeledDataset.read_file(store, engine="zarr")

Deprecations (old still works, warns — update when convenient)#

5. Constructing the base NetCDF(...) directly is deprecated. Use the typed entry points.

# Before
nc = NetCDF(gdal_dataset)

# After
nc  = NetCDF.read_file("cube.nc")     # -> Container
var = nc.get_variable("t")            # -> Variable

6. get_variable_names() -> the variable_names property.

# Before
names = nc.get_variable_names()
# After
names = nc.variable_names

7. ColourOpts -> ColorOpts.

# Before
from pyramids.netcdf import ColourOpts
opts = ColourOpts(cmap="viridis")
# After
from pyramids.netcdf import ColorOpts
opts = ColorOpts(cmap="viridis")

8. Classic dimension models renamed.

# Before
from pyramids.netcdf.dimensions import MetaData, DimMetaData
# After
from pyramids.netcdf.dimensions import ClassicDimMetadata, ClassicDimensionInfo

9. kerchunk backend="kerchunk" -> backend="legacy".

# Before
nc.to_kerchunk("refs.json", backend="kerchunk")
# After
nc.to_kerchunk("refs.json", backend="legacy")   # or omit backend= for the native default

Renames where the old name still works (no rush)#

10. Promoted internals (underscore aliases kept).

# Before
from pyramids.netcdf.labeled import _LabeledArray
from pyramids.netcdf._lazy import _apply_unpack
# After
from pyramids.netcdf import LabeledArray
from pyramids.netcdf._lazy import apply_unpack

Also: the private modules _kerchunk.py -> _kerchunk_facade.py and _kerchunk_native.py -> _kerchunk_builder.py were renamed. They are internal — use NetCDF.to_kerchunk / NetCDF.combine_kerchunk rather than importing them.

New, opt-in capabilities (not breaking)#

  • Typed dispatch: branch on isinstance(x, Container) vs isinstance(x, Variable) instead of inspecting is_subset / band_count. Import from pyramids.netcdf or pyramids.netcdf.variable.
  • Cloud read tuning: CloudConfig(vsicurl_tuning=True, curl_cache_size=...) enables the fast single-file /vsicurl/ read preset.

Naming note#

The new public types are named Container and Variable. They read cleanly when namespace-qualified (pyramids.netcdf.Container). If you from pyramids.netcdf import Variable in a module that also uses xarray, the name collides conceptually with xarray.Variable — prefer the namespace-qualified form, or alias on import:

from pyramids.netcdf import Variable as NcVariable

Migration checklist#

  1. Pin the new pyramids version in your dependencies.
  2. Run your suite under -W error::DeprecationWarning and fix everything it flags (items 5-10 above).
  3. Fix the hard-behavior-change items (1-4) — they do not warn: search for type(x) is NetCDF, type() / Dataset checks on subset() results, CFInfo mutation, and unrecognised LabeledDataset engines.
  4. Done — isinstance(x, NetCDF) keeps working, so most code needs no change.