Skip to content
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ make lockfiles
10. **Environment Reproducibility**: Set CPU features for consistent results:
`export NPY_DISABLE_CPU_FEATURES="AVX512F,AVX512CD,AVX512_SKX"`

11. **Pytest in Conda Env**: If `conda run -n <env> pytest ...` shows NumPy
re-import errors (e.g. "cannot load module more than once per process"),
run tests from an activated shell instead:
`source /opt/conda/etc/profile.d/conda.sh && conda activate <env> && pytest ...`
E.g. this error appears when using pytest-cov.


## Getting Help

Expand Down
1 change: 1 addition & 0 deletions lib/iris/fileformats/cf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,7 @@ def _span_check(
# those formula terms that are reference surface/phenomenon.
for cf_var in self.cf_group.formula_terms.values():
if iris.FUTURE.derived_bounds:
# TODO: is this supposed to be an isinstance()?
if self.cf_group[cf_var.cf_name] is CFBoundaryVariable:
continue
for cf_root, cf_term in cf_var.cf_terms_by_root.items():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Test the cf module."""
"""Integration tests for :mod:`iris.fileformats.cf`."""

import contextlib
import io
from typing import Iterable

import pytest
Expand All @@ -30,37 +28,6 @@ def fetch_cfvar_data(var, indices=()):
return data


class TestCaching:
def test_cached(self, mocker):
# Make sure attribute access to the underlying netCDF4.Variable
# is cached.
name = "foo"
nc_var = mocker.MagicMock()
cf_var = cf.CFAncillaryDataVariable(name, nc_var)
assert nc_var.ncattrs.call_count == 1

# Accessing a netCDF attribute should result in no further calls
# to nc_var.ncattrs() and the creation of an attribute on the
# cf_var.
# NB. Can't use hasattr() because that triggers the attribute
# to be created!
assert "coordinates" not in cf_var.__dict__
_ = cf_var.coordinates
assert nc_var.ncattrs.call_count == 1
assert "coordinates" in cf_var.__dict__

# Trying again results in no change.
_ = cf_var.coordinates
assert nc_var.ncattrs.call_count == 1
assert "coordinates" in cf_var.__dict__

# Trying another attribute results in just a new attribute.
assert "standard_name" not in cf_var.__dict__
_ = cf_var.standard_name
assert nc_var.ncattrs.call_count == 1
assert "standard_name" in cf_var.__dict__


@_shared_utils.skip_data
class TestCFReader:
@pytest.fixture(autouse=True)
Expand Down Expand Up @@ -245,30 +212,6 @@ def test_variable_attribute_touch_pass_0(self):
("units", "degrees_north"),
)

def test_destructor(self, tmp_path):
"""Test the destructor when reading the dataset fails.
Related to issue #3312: previously, the `CFReader` would
always call `close()` on its `_dataset` attribute, even if it
didn't exist because opening the dataset had failed.
"""
fn = tmp_path / "tmp.nc"
with fn.open("wb+") as fh:
fh.write(b"\x89HDF\r\n\x1a\nBroken file with correct signature")
fh.flush()

with io.StringIO() as buf:
with contextlib.redirect_stderr(buf):
try:
_ = cf.CFReader(str(fn))
except OSError:
pass
try:
_ = iris.load_cubes(str(fn))
except OSError:
pass
buf.seek(0)
assert buf.read() == ""


@_shared_utils.skip_data
class TestLoad:
Expand Down
53 changes: 53 additions & 0 deletions lib/iris/tests/unit/fileformats/cf/conftest.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ESadek-MO FYI in case you wanted to know

There should be a conftest.py file in the root/unit and root/integration folders. Additional lower level conftests can be added if it is agreed there is a need.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Shared fixtures for CF fileformat unit tests."""

import re

import numpy as np
import pytest


class _NetCDFVariableStub:
"""Minimal netCDF-like variable object for CF identify tests."""

def __init__(self, name, dtype):
self.name = name
self.dtype = np.dtype(dtype)

def ncattrs(self):
return [
attr
for attr in self.__dict__
if not attr.startswith("_") and attr not in ["name", "dtype"]
]


@pytest.fixture
def named_variable():
def _factory(name, dtype=int):
return _NetCDFVariableStub(name=name, dtype=dtype)

return _factory


@pytest.fixture
def assert_warning_gated():
def _assert(operation, warning_category, warning_regex):
with pytest.warns(warning_category, match=warning_regex):
operation(warn=True)

try:
with pytest.warns(warning_category, match=warning_regex):
operation(warn=False)
except pytest.fail.Exception:
pass
else:
pytest.fail(
f"Operation {operation.__name__} raised {warning_category.__name__} "
"when warn=False"
)

return _assert
Loading
Loading