Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
- name: Run unit tests
shell: bash -l {0}
run: |
pytest --cov=geotiepoints geotiepoints/tests --cov-report=xml --cov-report=
pytest --cov=geotiepoints geotiepoints --cov-report=xml --cov-report=

- name: Upload unittest coverage to Codecov
uses: codecov/codecov-action@v7
Expand Down
22 changes: 19 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ graph without computing.
```bash
pip install -e .
python setup.py build_ext --inplace --cython-coverage --force # required before running tests
pytest geotiepoints/tests
pytest --cov=geotiepoints geotiepoints/tests --cov-report=xml # what CI runs
pytest # whole package, doctests included
pytest --cov=geotiepoints geotiepoints --cov-report=xml # what CI runs
make -C doc doctest
```

Expand All @@ -135,7 +135,21 @@ make -C doc doctest

- Tests load HDF5 fixtures by path relative to the test file (`../../testdata/`), so they only work
from a source checkout, never from an installed wheel.
- There is **no `conftest.py` and no pytest configuration at all** — no markers, no ini options.
- There is **no `conftest.py`**. All pytest configuration lives in `[tool.pytest.ini_options]` in
`pyproject.toml` (mirroring trollimage): `--doctest-modules` plus `-ra --showlocals
--strict-markers --strict-config`, `xfail_strict`, `filterwarnings = ["error"]`, and
`testpaths = ["geotiepoints"]`. So a bare `pytest` runs the unit tests *and* every module
docstring. No markers. Two consequences worth knowing:
- Passing an explicit path (e.g. `pytest geotiepoints/tests`) overrides `testpaths` and skips the
package doctests.
- **Warnings are errors**, with one scoped exception: the `invalid value encountered in
arcsin/arccos` `RuntimeWarning` from `geotiepoints.geointerpolator`. `xyz2lonlat` is *meant* to
return NaN for coordinates that interpolation or extrapolation puts off the sphere -- an invalid
pixel should stay visibly invalid rather than be clipped to a plausible-looking lat/lon -- while
callers (Satpy readers) keep processing the rest of the swath. Do not "fix" that NaN.
- `warnings.catch_warnings(record=True)` inherits the error filter and will raise instead of
recording, so it needs an explicit `warnings.simplefilter("always")` inside the context (see
`test_modisinterpolator.test_sat_angle_based_interp`).
- `test_simple_modis_interpolator.py` imports its loaders and `assert_geodetic_distance` from
`test_modisinterpolator.py`. Preserve that cross-module dependency.
- `testdata/create_modis_test_data.py` regenerates fixtures but needs `pyhdf` plus a real MOD03 file;
Expand All @@ -145,6 +159,8 @@ make -C doc doctest
- CI: ubuntu/macos/windows × Python 3.11/3.12/3.13, plus an experimental nightly-dependency job.
`python_requires >= 3.11`.
- There is no `[project]` table — package metadata still lives in `setup.py`.
- `doc/source/conf.py` mocks nothing; the docs (and RTD, via `pip install .`) need the real package
and its dependencies importable, which is what makes the `index.rst` doctests runnable.
- `geotiepoints/version.py` is versioneer-generated; never edit it. Release steps are in `RELEASING.md`.

## Known defects and traps
Expand Down
24 changes: 0 additions & 24 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,6 @@
sys.path.insert(0, os.path.abspath('../../geotiepoints'))


class Mock(object):
def __init__(self, *args, **kwargs):
pass

def __call__(self, *args, **kwargs):
return Mock()

@classmethod
def __getattr__(cls, name):
if name in ('__file__', '__path__'):
return '/dev/null'
if name[0] == name[0].upper():
mockType = type(name, (), {})
mockType.__module__ = __name__
return mockType
return Mock()


MOCK_MODULES = ['numpy', 'scipy.interpolate', 'scipy',
'pyhdf.SD', 'pyhdf.error']
for mod_name in MOCK_MODULES:
sys.modules[mod_name] = Mock()


# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
Expand Down
4 changes: 2 additions & 2 deletions geotiepoints/interpolator.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ def _linear_extrapolate(pos, data, xev):
>>> data = np.arange(10).reshape((2, 5), order="F")
>>> xev = 5
>>> retv = _linear_extrapolate(pos, data, xev)
>>> print([val for val in retv])
>>> print([float(val) for val in retv])
[4.0, 6.0, 8.0, 10.0, 12.0]
>>> xev = 0
>>> retv = _linear_extrapolate(pos, data, xev)
>>> print([val for val in retv])
>>> print([float(val) for val in retv])
[-1.0, 1.0, 3.0, 5.0, 7.0]
"""
if len(data) != 2 or len(pos) != 2:
Expand Down
1 change: 1 addition & 0 deletions geotiepoints/tests/test_modisinterpolator.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def test_sat_angle_based_interp(input_func, exp_func, interp_func, dist_max, exp

# when working with dask arrays, we shouldn't compute anything
with dask.config.set(scheduler=CustomScheduler(0)), warnings.catch_warnings(record=True) as warns:
warnings.simplefilter("always")
lons, lats = interp_func(lon1, lat1, satz1)
has_5km_warning = any("may result in poor quality" in str(w.message) for w in warns)
if exp_5km_warning:
Expand Down
5 changes: 4 additions & 1 deletion geotiepoints/tests/test_multilinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ def test_multilinear_interp(self):
])

interp = MultilinearInterpolator(smin, smax, orders)
interp.set_values(f(interp.grid))
with np.errstate(invalid="ignore"):
# x**3 + y**3 is negative over part of the grid; the resulting NaNs are
# baked into the expected RES1 values.
interp.set_values(f(interp.grid))

result = interp(ARR1)
# exact_values = f(ARR1)
Expand Down
15 changes: 15 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@
requires = ["setuptools", "wheel", "numpy>=2.0.0,<3", "Cython>=3.1.2", "versioneer[toml]"]
build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
minversion = "6.0"
addopts = ["-ra", "--showlocals", "--strict-markers", "--strict-config", "--doctest-modules"]
xfail_strict = true
filterwarnings = [
"error",
# xyz2lonlat: coordinates that interpolation/extrapolation puts off the sphere make the
# arcsin/arccos argument exceed 1. NaN is the intended result there -- an invalid pixel should
# be visible as NaN rather than silently corrected -- and callers (Satpy readers) must keep
# processing the rest of the swath, so the accompanying RuntimeWarning is not an error.
"ignore:invalid value encountered in arc(sin|cos):RuntimeWarning:geotiepoints.geointerpolator",
]
log_cli_level = "info"
testpaths = ["geotiepoints"]

[tool.coverage.run]
relative_files = true
plugins = ["Cython.Coverage"]
Expand Down
Loading