PyOPIA 2.16.14__tar.gz → 2.16.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. {pyopia-2.16.14 → pyopia-2.16.16}/.gitignore +1 -1
  2. {pyopia-2.16.14 → pyopia-2.16.16}/PKG-INFO +51 -35
  3. {pyopia-2.16.14 → pyopia-2.16.16}/README.md +46 -33
  4. pyopia-2.16.16/pyopia/__init__.py +1 -0
  5. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/auxillarydata.py +14 -7
  6. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/cli.py +198 -93
  7. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/instrument/holo.py +1 -1
  8. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/pipeline.py +3 -1
  9. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/statistics.py +11 -2
  10. pyopia-2.16.16/pyopia/tests/conftest.py +186 -0
  11. pyopia-2.16.16/pyopia/tests/test_classify.py +229 -0
  12. pyopia-2.16.16/pyopia/tests/test_cli.py +290 -0
  13. pyopia-2.16.16/pyopia/tests/test_notebooks.py +85 -0
  14. pyopia-2.16.16/pyopia/tests/test_pipeline.py +344 -0
  15. {pyopia-2.16.14 → pyopia-2.16.16}/pyproject.toml +22 -0
  16. pyopia-2.16.14/pyopia/__init__.py +0 -1
  17. pyopia-2.16.14/pyopia/tests/test_classify.py +0 -256
  18. pyopia-2.16.14/pyopia/tests/test_notebooks.py +0 -26
  19. pyopia-2.16.14/pyopia/tests/test_pipeline.py +0 -330
  20. {pyopia-2.16.14 → pyopia-2.16.16}/LICENSE +0 -0
  21. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/background.py +0 -0
  22. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/cf_metadata.json +0 -0
  23. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/classify.py +0 -0
  24. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/classify_torch.py +0 -0
  25. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/dataexport/__init__.py +0 -0
  26. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/dataexport/ecotaxa.py +0 -0
  27. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/exampledata.py +0 -0
  28. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/instrument/__init__.py +0 -0
  29. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/instrument/common.py +0 -0
  30. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/instrument/silcam.py +0 -0
  31. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/instrument/uvp.py +0 -0
  32. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/io.py +0 -0
  33. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/metadata.py +0 -0
  34. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/plotting.py +0 -0
  35. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/process.py +0 -0
  36. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/realtime.py +0 -0
  37. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/simulator/__init__.py +0 -0
  38. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/simulator/silcam.py +0 -0
  39. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/tests/__init__.py +0 -0
  40. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/tests/test_auxillarydata.py +0 -0
  41. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/tests/test_io.py +0 -0
  42. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/tests/test_process.py +0 -0
  43. {pyopia-2.16.14 → pyopia-2.16.16}/pyopia/tests/test_realtime.py +0 -0
@@ -267,7 +267,7 @@ poetry.lock
267
267
  /notebooks/oil_silcam_images
268
268
  header.tfl.txt
269
269
  *.tiff
270
- notebooks/__MACOSX/*
270
+ **/__MACOSX/*
271
271
  notebooks/silcam240822.keras
272
272
  model/*
273
273
  notebooks/model/*
@@ -1,10 +1,11 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: PyOPIA
3
- Version: 2.16.14
3
+ Version: 2.16.16
4
4
  Summary: A Python Ocean Particle Image Analysis toolbox.
5
5
  Project-URL: Repository, https://github.com/sintef/pyopia
6
6
  Project-URL: Documentation, https://pyopia.readthedocs.io
7
7
  Author-email: Emlyn Davies <emlyn.davies@sintef.no>, Alex Nimmo Smith <alex@nimmosmith.co.uk>
8
+ License-Expression: BSD-3-Clause
8
9
  License-File: LICENSE
9
10
  Keywords: Imaging,Measurement,Ocean,Particles,Size distribution
10
11
  Requires-Python: ~=3.12.0
@@ -27,6 +28,8 @@ Requires-Dist: pandas[computation]<3,>=2.1.1
27
28
  Requires-Dist: poetry-version-plugin<0.3,>=0.2.0
28
29
  Requires-Dist: pydantic>=2.11.7
29
30
  Requires-Dist: pytest-error-for-skips<3,>=2.0.2
31
+ Requires-Dist: pytest-rerunfailures<15,>=14.0
32
+ Requires-Dist: pytest-timeout<3,>=2.3.1
30
33
  Requires-Dist: pytest>=7.2.0
31
34
  Requires-Dist: readthedocs-sphinx-search<0.4,>=0.3.2
32
35
  Requires-Dist: scikit-image<0.25,>=0.24.0
@@ -55,10 +58,12 @@ PyOPIA
55
58
 
56
59
  A Python Ocean Particle Image Analysis toolbox
57
60
 
61
+ PyOPIA processes images of particles suspended in water (e.g. from SilCam, holographic, or UVP imaging systems) into particle size, shape, and concentration statistics.
62
+
58
63
  # Quick tryout of PyOPIA
59
64
 
60
65
  1) Install [uv](https://docs.astral.sh/uv/getting-started/installation)
61
- 2) Initialize PyOPIA project with a small example image dataset and run processing
66
+ 2) Initialize a PyOPIA project with example data (`--example-data` downloads a small example image dataset and generates a matching `config.toml`), then run processing:
62
67
  ```bash
63
68
  uvx --python 3.12 --from pyopia[classification] pyopia init-project pyopiatest --example-data
64
69
  ```
@@ -76,19 +81,15 @@ uvx --python 3.12 --from pyopia[classification] pyopia merge-mfdata processed
76
81
  ```
77
82
 
78
83
  ```bash
79
- uvx --python 3.12 --from pyopia[classification] pyopia make-montage processed\pyopiatest-STATS.nc
80
- ```
81
- 5) Visualise the monatge of all processed singular particle images in one
82
- ```bash
83
- montage.png
84
+ uvx --python 3.12 --from pyopia[classification] pyopia make-montage processed/pyopiatest-STATS.nc
84
85
  ```
85
- Will show you a montage of all the processed particle images in one.
86
+ 5) This creates `montage.png` in the current folder - open it to see a single image made up of all the processed particle images.
86
87
 
87
88
  See the documentation for more information on how to install and use PyOPIA.
88
89
 
89
90
  # Running with Docker
90
91
 
91
- A prebuilt container image is published to GitHub Container Registry on every release, for users who prefer not to install PyOPIA's dependencies directly.
92
+ A prebuilt container image is published to GitHub Container Registry on every release. Docker is worth reaching for if you'd rather not install PyOPIA's dependencies directly: it avoids the install overhead of heavier optional dependencies (e.g. TensorFlow/PyTorch for classification), guarantees a consistent, reproducible environment regardless of your host OS, and is well suited to running on servers or HPC systems.
92
93
 
93
94
  ## One-off invocation
94
95
 
@@ -161,41 +162,57 @@ PYOPIA_CONFIG=my_run.toml docker compose run --rm pyopia
161
162
  [pyopia.readthedocs.io](https://pyopia.readthedocs.io)
162
163
  # Current status:
163
164
 
164
- - Under development. See/regester issues, [here](https://github.com/SINTEF/pyopia/issues)
165
+ - Under development. See/register issues, [here](https://github.com/SINTEF/pyopia/issues)
165
166
 
166
167
  ----
167
- # Development targets for PyOpia:
168
+ # Releases
168
169
 
169
- 1) Allow nonfamiliar users to install and use PyOpia, and to contribute & commit code changes
170
- 2) Not hardware specific
171
- 3) Smaller dependency list than PySilCam -Eventual optional dependencies (e.g. for classification)
172
- 4) Can be imported by pysilcam or other hardware-specific tools
173
- 5) Work on a single-image basis (...primarily, with options for multiprocess to be considered later)
174
- 6) No use of settings/config files within the core code - pass arguments directly. Eventual use of settings/config files should be handled by high-level wrappers that provide arguments to functions.
175
- 7) Github workflows
176
- 8) Tests
170
+ Releases are cut when a finished, documented, and tested piece of work is ready to be used, rather than on a fixed schedule. If a feature has landed on `main` and meets the Contributions guidelines below, it should go out in the next release rather than sit unreleased. We aim to keep the Docker image (see Running with Docker above) in sync with each release, publishing it as part of the release process rather than as a separate manual step.
177
171
 
178
- Normal functions within PyOpia should:
172
+ ----
173
+ # Design principles
179
174
 
180
- 1) take inputs
181
- 2) return new outputs
182
- 3) don't modify state of input
183
- 4) minimum possible disc IO during processing
175
+ - PyOPIA is instrument-agnostic at its core: SilCam, holographic, and UVP support are all built as pluggable instrument modules on top of a shared `Pipeline`.
176
+ - Processing is config-driven: a `Pipeline` is built from a TOML/dict `settings` object describing an ordered list of steps, each mapping to a Python class. Steps update a shared `data` dict as the pipeline runs.
177
+ - Heavy, optional dependencies (e.g. TensorFlow/PyTorch for classification) are kept out of the core install and available via extras (`pyopia[classification]`, `pyopia[classification-torch]`).
178
+ - Multi-file, multi-core processing is supported via chunked/parallel processing (`pyopia process --num-chunks`).
184
179
 
185
180
  ## Contributions
186
181
 
187
182
  We welcome additions and improvements to the code! We request that you follow a few guidelines. These are in place to make sure the code improves over time.
188
183
 
189
184
  1. All code changes must be submitted as pull requests, either from a branch or a fork.
190
- 2. Good documentation of the code is needed for PyOpia to succeed and so please include up-to-date docstrings as you make changes, so that the auto-build on readthedocs is complete and useful for users. (A version of the new docs will complie when you make a pull request and a link to this can be found in the pull request checks)
185
+ 2. Good documentation of the code is needed for PyOPIA to succeed and so please include up-to-date docstrings as you make changes, so that the auto-build on readthedocs is complete and useful for users. (A version of the new docs will compile when you make a pull request and a link to this can be found in the pull request checks)
191
186
  3. All pull requests are required to pass all tests before merging. Please do not disable or remove tests just to make your branch pass the pull request.
192
- 4. All pull requests must be reviewed by a person. The benefits from code review are plenty, but we like to emphasise that code reviews help spreading the awarenes of code changes. Please note that code reviews should be a pleasant experience, so be plesant, polite and remember that there is a human being with good intentions on the other side of the screen.
187
+ 4. All pull requests must be reviewed by a person. The benefits from code review are plenty, but we like to emphasise that code reviews help spreading the awareness of code changes. Please note that code reviews should be a pleasant experience, so be pleasant, polite and remember that there is a human being with good intentions on the other side of the screen.
193
188
  5. All contributions are linted with flake8. We recommend that you run flake8 on your code while developing to fix any issues as you go. We recommend using autopep8 to autoformat your Python code (but please check the code behaviour is not affected by autoformatting before pushing). This makes flake8 happy, and makes it easier for us all to maintain a consistent and readable code base.
194
189
 
190
+ ### Using AI tools
191
+
192
+ AI coding tools (e.g. Claude, Copilot, ChatGPT) are welcome as an aid to writing PyOPIA contributions. They are tools, not authors: whoever submits a pull request is responsible for the code in it, regardless of how much of it an AI tool helped produce. Please review, understand, and test any AI-assisted changes yourself before submitting them - the same guidelines above (documentation, tests, review, flake8) apply either way.
193
+
195
194
  ## Docstrings
196
195
 
197
196
  Use the NumPy style in docstrings. See style guide [here](https://numpydoc.readthedocs.io/en/latest/format.html#documenting-classes)
198
197
 
198
+ ## Testing
199
+
200
+ PyOPIA's test suite lives in `pyopia/tests/` and runs via `pytest` (see `uv run pytest` below). A few things are useful to know before running or adding to it:
201
+
202
+ **Markers**: some tests are tagged with pytest markers to indicate how expensive they are.
203
+
204
+ - `@pytest.mark.slow` - tests that do real network downloads and/or real model inference (e.g. downloading the example classifier model and running real predictions on it). These run in routine CI, but you can skip them for a fast local feedback loop:
205
+ ```bash
206
+ uv run pytest -m "not slow"
207
+ ```
208
+ Unmarked tests are fast and have no external dependencies; they always run.
209
+
210
+ **Shared fixtures**: tests that need real example data (an example image, the trained classifier model, the classifier training database, an example hologram) get it from session-scoped fixtures defined in `pyopia/tests/conftest.py`, rather than each downloading their own copy. The download happens once per test run and is shared across every test file that needs it.
211
+
212
+ **Notebooks**: `pyopia/tests/test_notebooks.py` executes the notebooks in `notebooks/` and `docs/notebooks/` to check they still run against the current codebase. Each notebook is its own parametrized test (`test_notebook[<name>.ipynb]`), tagged `slow` as above where relevant. Not every notebook is included - a couple depend on state produced by another notebook, or by a user's own prior processing run, and would fail if executed standalone; see the comments in `test_notebooks.py` for which ones and why. These tests also get a longer timeout (1800s) and up to 2 automatic retries on failure, since running a Jupyter kernel via nbconvert has shown real, platform-specific flakiness on macOS CI runners rather than a reproducible bug.
213
+
214
+ Please do not disable or remove tests just to make a pull request pass - see Contributions guideline 3 above.
215
+
199
216
  # Installing
200
217
 
201
218
  ## For users
@@ -205,7 +222,7 @@ Users are expected to be familiar with Python. Please refer to the recommended i
205
222
  ## For developers from source
206
223
 
207
224
 
208
- Install (uv)[https://docs.astral.sh/uv/getting-started/installation/]
225
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/)
209
226
 
210
227
  1. Navigate to the folder where you want to install pyopia using the 'cd' command.
211
228
 
@@ -222,10 +239,10 @@ For the next steps, you need to be located in the PyOPIA root directory that con
222
239
  2. Install all requirements with
223
240
 
224
241
  ```bash
225
- uv sync --all-extras
242
+ uv sync --all-extras --dev
226
243
  ```
227
244
 
228
- 3. (optional) Run local tests:
245
+ 3. (optional) Run local tests (see the Testing section above for markers and how to run a fast subset):
229
246
 
230
247
  ```bash
231
248
  uv run pytest
@@ -241,13 +258,12 @@ The version number of PyOPIA is split into three sections: MAJOR.MINOR.PATCH
241
258
 
242
259
  ## Build docs locally
243
260
 
244
- ```
245
- sphinx-apidoc -f -o docs/source docs/build --separate
246
-
247
- sphinx-build -b html ./docs/ ./docs/build
261
+ ```bash
262
+ uv sync --extra classification --group docs
263
+ uv run sphinx-build -b html docs/ docs/_build/html
248
264
  ```
249
265
 
250
266
  ----
251
267
  # License
252
268
 
253
- PyOpia is licensed under the BSD3 license. See LICENSE. All contributors should be recognised & aknowledged.
269
+ PyOPIA is licensed under the BSD3 license. See LICENSE. All contributors should be recognised & acknowledged.
@@ -3,10 +3,12 @@ PyOPIA
3
3
 
4
4
  A Python Ocean Particle Image Analysis toolbox
5
5
 
6
+ PyOPIA processes images of particles suspended in water (e.g. from SilCam, holographic, or UVP imaging systems) into particle size, shape, and concentration statistics.
7
+
6
8
  # Quick tryout of PyOPIA
7
9
 
8
10
  1) Install [uv](https://docs.astral.sh/uv/getting-started/installation)
9
- 2) Initialize PyOPIA project with a small example image dataset and run processing
11
+ 2) Initialize a PyOPIA project with example data (`--example-data` downloads a small example image dataset and generates a matching `config.toml`), then run processing:
10
12
  ```bash
11
13
  uvx --python 3.12 --from pyopia[classification] pyopia init-project pyopiatest --example-data
12
14
  ```
@@ -24,19 +26,15 @@ uvx --python 3.12 --from pyopia[classification] pyopia merge-mfdata processed
24
26
  ```
25
27
 
26
28
  ```bash
27
- uvx --python 3.12 --from pyopia[classification] pyopia make-montage processed\pyopiatest-STATS.nc
28
- ```
29
- 5) Visualise the monatge of all processed singular particle images in one
30
- ```bash
31
- montage.png
29
+ uvx --python 3.12 --from pyopia[classification] pyopia make-montage processed/pyopiatest-STATS.nc
32
30
  ```
33
- Will show you a montage of all the processed particle images in one.
31
+ 5) This creates `montage.png` in the current folder - open it to see a single image made up of all the processed particle images.
34
32
 
35
33
  See the documentation for more information on how to install and use PyOPIA.
36
34
 
37
35
  # Running with Docker
38
36
 
39
- A prebuilt container image is published to GitHub Container Registry on every release, for users who prefer not to install PyOPIA's dependencies directly.
37
+ A prebuilt container image is published to GitHub Container Registry on every release. Docker is worth reaching for if you'd rather not install PyOPIA's dependencies directly: it avoids the install overhead of heavier optional dependencies (e.g. TensorFlow/PyTorch for classification), guarantees a consistent, reproducible environment regardless of your host OS, and is well suited to running on servers or HPC systems.
40
38
 
41
39
  ## One-off invocation
42
40
 
@@ -109,41 +107,57 @@ PYOPIA_CONFIG=my_run.toml docker compose run --rm pyopia
109
107
  [pyopia.readthedocs.io](https://pyopia.readthedocs.io)
110
108
  # Current status:
111
109
 
112
- - Under development. See/regester issues, [here](https://github.com/SINTEF/pyopia/issues)
110
+ - Under development. See/register issues, [here](https://github.com/SINTEF/pyopia/issues)
113
111
 
114
112
  ----
115
- # Development targets for PyOpia:
113
+ # Releases
116
114
 
117
- 1) Allow nonfamiliar users to install and use PyOpia, and to contribute & commit code changes
118
- 2) Not hardware specific
119
- 3) Smaller dependency list than PySilCam -Eventual optional dependencies (e.g. for classification)
120
- 4) Can be imported by pysilcam or other hardware-specific tools
121
- 5) Work on a single-image basis (...primarily, with options for multiprocess to be considered later)
122
- 6) No use of settings/config files within the core code - pass arguments directly. Eventual use of settings/config files should be handled by high-level wrappers that provide arguments to functions.
123
- 7) Github workflows
124
- 8) Tests
115
+ Releases are cut when a finished, documented, and tested piece of work is ready to be used, rather than on a fixed schedule. If a feature has landed on `main` and meets the Contributions guidelines below, it should go out in the next release rather than sit unreleased. We aim to keep the Docker image (see Running with Docker above) in sync with each release, publishing it as part of the release process rather than as a separate manual step.
125
116
 
126
- Normal functions within PyOpia should:
117
+ ----
118
+ # Design principles
127
119
 
128
- 1) take inputs
129
- 2) return new outputs
130
- 3) don't modify state of input
131
- 4) minimum possible disc IO during processing
120
+ - PyOPIA is instrument-agnostic at its core: SilCam, holographic, and UVP support are all built as pluggable instrument modules on top of a shared `Pipeline`.
121
+ - Processing is config-driven: a `Pipeline` is built from a TOML/dict `settings` object describing an ordered list of steps, each mapping to a Python class. Steps update a shared `data` dict as the pipeline runs.
122
+ - Heavy, optional dependencies (e.g. TensorFlow/PyTorch for classification) are kept out of the core install and available via extras (`pyopia[classification]`, `pyopia[classification-torch]`).
123
+ - Multi-file, multi-core processing is supported via chunked/parallel processing (`pyopia process --num-chunks`).
132
124
 
133
125
  ## Contributions
134
126
 
135
127
  We welcome additions and improvements to the code! We request that you follow a few guidelines. These are in place to make sure the code improves over time.
136
128
 
137
129
  1. All code changes must be submitted as pull requests, either from a branch or a fork.
138
- 2. Good documentation of the code is needed for PyOpia to succeed and so please include up-to-date docstrings as you make changes, so that the auto-build on readthedocs is complete and useful for users. (A version of the new docs will complie when you make a pull request and a link to this can be found in the pull request checks)
130
+ 2. Good documentation of the code is needed for PyOPIA to succeed and so please include up-to-date docstrings as you make changes, so that the auto-build on readthedocs is complete and useful for users. (A version of the new docs will compile when you make a pull request and a link to this can be found in the pull request checks)
139
131
  3. All pull requests are required to pass all tests before merging. Please do not disable or remove tests just to make your branch pass the pull request.
140
- 4. All pull requests must be reviewed by a person. The benefits from code review are plenty, but we like to emphasise that code reviews help spreading the awarenes of code changes. Please note that code reviews should be a pleasant experience, so be plesant, polite and remember that there is a human being with good intentions on the other side of the screen.
132
+ 4. All pull requests must be reviewed by a person. The benefits from code review are plenty, but we like to emphasise that code reviews help spreading the awareness of code changes. Please note that code reviews should be a pleasant experience, so be pleasant, polite and remember that there is a human being with good intentions on the other side of the screen.
141
133
  5. All contributions are linted with flake8. We recommend that you run flake8 on your code while developing to fix any issues as you go. We recommend using autopep8 to autoformat your Python code (but please check the code behaviour is not affected by autoformatting before pushing). This makes flake8 happy, and makes it easier for us all to maintain a consistent and readable code base.
142
134
 
135
+ ### Using AI tools
136
+
137
+ AI coding tools (e.g. Claude, Copilot, ChatGPT) are welcome as an aid to writing PyOPIA contributions. They are tools, not authors: whoever submits a pull request is responsible for the code in it, regardless of how much of it an AI tool helped produce. Please review, understand, and test any AI-assisted changes yourself before submitting them - the same guidelines above (documentation, tests, review, flake8) apply either way.
138
+
143
139
  ## Docstrings
144
140
 
145
141
  Use the NumPy style in docstrings. See style guide [here](https://numpydoc.readthedocs.io/en/latest/format.html#documenting-classes)
146
142
 
143
+ ## Testing
144
+
145
+ PyOPIA's test suite lives in `pyopia/tests/` and runs via `pytest` (see `uv run pytest` below). A few things are useful to know before running or adding to it:
146
+
147
+ **Markers**: some tests are tagged with pytest markers to indicate how expensive they are.
148
+
149
+ - `@pytest.mark.slow` - tests that do real network downloads and/or real model inference (e.g. downloading the example classifier model and running real predictions on it). These run in routine CI, but you can skip them for a fast local feedback loop:
150
+ ```bash
151
+ uv run pytest -m "not slow"
152
+ ```
153
+ Unmarked tests are fast and have no external dependencies; they always run.
154
+
155
+ **Shared fixtures**: tests that need real example data (an example image, the trained classifier model, the classifier training database, an example hologram) get it from session-scoped fixtures defined in `pyopia/tests/conftest.py`, rather than each downloading their own copy. The download happens once per test run and is shared across every test file that needs it.
156
+
157
+ **Notebooks**: `pyopia/tests/test_notebooks.py` executes the notebooks in `notebooks/` and `docs/notebooks/` to check they still run against the current codebase. Each notebook is its own parametrized test (`test_notebook[<name>.ipynb]`), tagged `slow` as above where relevant. Not every notebook is included - a couple depend on state produced by another notebook, or by a user's own prior processing run, and would fail if executed standalone; see the comments in `test_notebooks.py` for which ones and why. These tests also get a longer timeout (1800s) and up to 2 automatic retries on failure, since running a Jupyter kernel via nbconvert has shown real, platform-specific flakiness on macOS CI runners rather than a reproducible bug.
158
+
159
+ Please do not disable or remove tests just to make a pull request pass - see Contributions guideline 3 above.
160
+
147
161
  # Installing
148
162
 
149
163
  ## For users
@@ -153,7 +167,7 @@ Users are expected to be familiar with Python. Please refer to the recommended i
153
167
  ## For developers from source
154
168
 
155
169
 
156
- Install (uv)[https://docs.astral.sh/uv/getting-started/installation/]
170
+ Install [uv](https://docs.astral.sh/uv/getting-started/installation/)
157
171
 
158
172
  1. Navigate to the folder where you want to install pyopia using the 'cd' command.
159
173
 
@@ -170,10 +184,10 @@ For the next steps, you need to be located in the PyOPIA root directory that con
170
184
  2. Install all requirements with
171
185
 
172
186
  ```bash
173
- uv sync --all-extras
187
+ uv sync --all-extras --dev
174
188
  ```
175
189
 
176
- 3. (optional) Run local tests:
190
+ 3. (optional) Run local tests (see the Testing section above for markers and how to run a fast subset):
177
191
 
178
192
  ```bash
179
193
  uv run pytest
@@ -189,13 +203,12 @@ The version number of PyOPIA is split into three sections: MAJOR.MINOR.PATCH
189
203
 
190
204
  ## Build docs locally
191
205
 
192
- ```
193
- sphinx-apidoc -f -o docs/source docs/build --separate
194
-
195
- sphinx-build -b html ./docs/ ./docs/build
206
+ ```bash
207
+ uv sync --extra classification --group docs
208
+ uv run sphinx-build -b html docs/ docs/_build/html
196
209
  ```
197
210
 
198
211
  ----
199
212
  # License
200
213
 
201
- PyOpia is licensed under the BSD3 license. See LICENSE. All contributors should be recognised & aknowledged.
214
+ PyOPIA is licensed under the BSD3 license. See LICENSE. All contributors should be recognised & acknowledged.
@@ -0,0 +1 @@
1
+ __version__ = "2.16.16"
@@ -4,6 +4,11 @@ import xarray as xr
4
4
 
5
5
  logger = logging.getLogger()
6
6
 
7
+
8
+ class AuxillaryDataError(Exception):
9
+ """Raised when an auxiliary data file exists but cannot be parsed into the expected format."""
10
+
11
+
7
12
  AUXILLARY_DATA_FILE_TEMPLATE = """% COMMENT LINE: PLEASE UPDATE THIS FILE WITH PROJECT RELEVANT DATA. EACH COLUMN WILL BECOME A NETCDF VARIABLE.
8
13
  % COMMENT LINE: ONE LINE PER MEASUREMENT, TIME IS INTERPOLATED TO IMAGE DATA TIMES IN PYOPIA. FOLLOWING LINES ARE UNITS, DESCRIPTION AND VARIABLE NAME.
9
14
  ,metres,degC
@@ -50,17 +55,19 @@ class AuxillaryData:
50
55
  def __init__(self, auxillary_data_path=None):
51
56
  self.auxillary_data_path = auxillary_data_path
52
57
 
53
- # Create empty dataframe for cases where no file was specified, or an error occured reading it
58
+ # Create empty dataframe for cases where no file was specified
54
59
  self.auxillary_data = pd.DataFrame(index=pd.Index([], name="time")).to_xarray()
55
60
  if auxillary_data_path is not None:
56
61
  try:
57
62
  self.auxillary_data = self.load_auxillary_data(auxillary_data_path)
58
- except RuntimeError as e:
59
- print(f"Failed to load auxillary data from file: {self.auxillary_data}")
60
- logging.error(
61
- f"Failed to load auxillary data from file: {self.auxillary_data}"
62
- )
63
- logging.error(e)
63
+ except Exception as e:
64
+ # Re-raise as a single, specific error type that names the file and the
65
+ # underlying cause, so callers can recognise this failure mode (the same
66
+ # auxiliary data file applies to every image, so it is not worth retrying)
67
+ # and report something more useful than a bare KeyError/ValueError.
68
+ raise AuxillaryDataError(
69
+ f"Failed to load auxiliary data from '{auxillary_data_path}': {e}"
70
+ ) from e
64
71
 
65
72
  def load_auxillary_data(self, auxillary_data_path):
66
73
  """Load and format uxillary data from .csv file"""