Skip to content

Rename fixture recipes to *_recipe.py and add --pds3-label-method#25

Merged
rfrenchseti merged 1 commit into
mainfrom
rf_260514
May 14, 2026
Merged

Rename fixture recipes to *_recipe.py and add --pds3-label-method#25
rfrenchseti merged 1 commit into
mainfrom
rf_260514

Conversation

@rfrenchseti
Copy link
Copy Markdown
Collaborator

@rfrenchseti rfrenchseti commented May 14, 2026

Summary

Two independent changes bundled because they touch the same areas:

  1. Rename fixture-recipe files to drop the embedded dot. Sixteen files under tests/fixture_recipes/ were named <name>.recipe.py, which made them unimportable as Python modules and confused tools that key on file basenames. They are now <name>_recipe.py. The recipe-runner glob (regenerate_all.py), each recipe's own python <name>_recipe.py docstring line, and docs/dev/adding_an_instrument.rst are updated to match.

  2. Plumb pds3_label_method through to pdsparser.PdsLabel so every call site receives an explicit method= argument. New canonical tuple PDS3_LABEL_METHODS = ('strict', 'loose', 'compound', 'fast') exported from picmaker.options; PicmakerOptions.pds3_label_method field validated against it.

Plumbing map

Layer Change
picmaker.options PDS3_LABEL_METHODS constant + PicmakerOptions.pds3_label_method: str = 'strict' + validate() check
picmaker.io read_image_array, read_one_image_array, read_pds_labeled_image_array each gain a keyword-only pds3_label_method='strict' and forward it (4 PdsLabel(...) call sites updated)
picmaker.pipeline _pds3_resolve_pointer gains the same keyword (1 PdsLabel(...) call updated); _process_one_image reads options.pds3_label_method and threads it into both call sites; images_to_pics gains a pds3_label_method kwarg that flows into the dataclass
picmaker.cli New --pds3-label-method / --pds3_label_method flag with choices=PDS3_LABEL_METHODS; threads through _normalize_and_validate into the option_dict

Tests and docs

  • tests/conftest.pybasic_option_dict fixture gains the new key.
  • tests/test_cli.py — help-flag regex tightened to include digits (--[a-z0-9_-]+) so digit-bearing flags like --16 and --pds3-label-method are captured whole; baseline regenerated (now also lists --16, which the old regex was silently dropping).
  • tests/test_cli_unit.py — new normalization tests: default is 'strict', parametrized over each accepted choice, rejects unknown via argparse.
  • tests/test_pipeline_helpers.pyPicmakerOptions.validate() accepts each documented value and rejects unknown with the expected message.
  • docs/user_guide.rst — new CLI flag documented under selection options.
  • docs/dev/pipeline.rstvalidate() invariant list updated.

Test plan

  • `pytest tests/` — 520 passed, 93.04% coverage
  • `ruff check src/ tests/` — clean
  • `mypy src tests` — clean
  • `pyroma` — 10/10
  • `bandit`, `vulture` — clean
  • `sphinx-build -W` — clean
  • `pymarkdown scan docs/ .cursor/ README.md CONTRIBUTING.md` — clean

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added --pds3-label-method command-line option to control PDS3 label parsing strictness for .LBL files. Supported modes: strict (default), loose, compound, and fast.
  • Documentation

    • Updated user guide and developer documentation to document the new PDS3 label parsing option and its supported parsing modes.
  • Tests

    • Added unit tests for the new PDS3 label method option validation.

Review Change Stack

* Rename every tests/fixture_recipes/<name>.recipe.py to
  <name>_recipe.py so the files are importable Python modules (the
  embedded dot made them unusable as `from ... import` targets and
  confused tools that key on basenames). Update the recipe-runner
  glob, the in-file `python ...` doc-comments, and the
  adding_an_instrument.rst example accordingly.

* Plumb a new `pds3_label_method` option through the call chain so
  every `pdsparser.PdsLabel(...)` call site receives an explicit
  `method=` argument:
    - `PicmakerOptions.pds3_label_method: str = 'strict'` with a
      validate() check against the canonical
      `PDS3_LABEL_METHODS = ('strict', 'loose', 'compound', 'fast')`
      tuple exported alongside the dataclass.
    - `read_image_array`, `read_one_image_array`, and
      `read_pds_labeled_image_array` each accept a keyword-only
      `pds3_label_method='strict'` and forward it to PdsLabel.
    - `_pds3_resolve_pointer` accepts the same keyword and forwards it
      to its own PdsLabel call.
    - `_process_one_image` reads `options.pds3_label_method` and threads
      it into both call sites.
    - `images_to_pics` gains a `pds3_label_method` keyword that flows
      into `PicmakerOptions`.
    - The CLI gains `--pds3-label-method` (and its underscore alias
      `--pds3_label_method`) with `choices=PDS3_LABEL_METHODS`.

* Tighten the help-flag baseline regex to include digits
  (`--[a-z0-9_-]+`) so `--16` and `--pds3-label-method` are captured
  whole; regenerate tests/fixtures/.baseline-flags.txt accordingly.

* Update user_guide.rst (CLI section) and dev/pipeline.rst (validate
  invariants list) with the new option. Add focused tests:
  PicmakerOptions validate() accepts each value and rejects unknown;
  CLI normalization defaults to 'strict', accepts each choice, and
  rejects unknown via argparse.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Walkthrough

This PR adds a configurable PDS3 label parsing method option that flows from the options layer through CLI, image I/O, and pipeline, including validation, documentation, and test coverage. Fixture recipe scripts also update their documentation to use the correct filename pattern.

Changes

PDS3 Label Parsing Method Configuration

Layer / File(s) Summary
Option definition and validation
src/picmaker/options.py
PDS3_LABEL_METHODS constant ('strict', 'loose', 'compound', 'fast') is defined and exported; PicmakerOptions.pds3_label_method field defaults to 'strict' and is validated against the allowed set in validate().
CLI argument and option dict wiring
src/picmaker/cli.py, tests/fixtures/.baseline-flags.txt, tests/test_cli.py
--pds3-label-method / --pds3_label_method argument is added to argparse with choices from PDS3_LABEL_METHODS; option is wired into option_dict for validation; baseline flags and CLI test regex are updated to match the new argument.
I/O parameter threading for PDS3 label reading
src/picmaker/io.py
read_image_array, read_one_image_array, and read_pds_labeled_image_array each gain a keyword-only pds3_label_method parameter (default 'strict') that is forwarded through the PDS3 parsing path and used when constructing pdsparser.PdsLabel(method=...).
Pipeline entry point and internal helper wiring
src/picmaker/pipeline.py
images_to_pics public entry point gains pds3_label_method parameter; _pds3_resolve_pointer and _process_one_image are updated to accept and forward the method into both label resolution and image reading paths.
User and developer documentation
docs/dev/adding_an_instrument.rst, docs/dev/pipeline.rst, docs/user_guide.rst
Developer guide, pipeline documentation, and user guide are updated to document the new pds3_label_method option, its allowed values, and how it is forwarded to pdsparser.PdsLabel.
Test fixture and configuration updates
tests/conftest.py
basic_option_dict test fixture is extended to include the new pds3_label_method: 'strict' default option.
CLI argument parsing and validation tests
tests/test_cli_unit.py
New unit tests verify that --pds3-label-method defaults to 'strict', accepts each documented choice while preserving the value, and rejects unknown methods via argparse SystemExit.
Pipeline option validation tests
tests/test_pipeline_helpers.py
New tests import PDS3_LABEL_METHODS, verify it contains the canonical tuple of methods, confirm PicmakerOptions.validate() accepts all supported methods, and validate that unknown methods raise ValueError with appropriate error messages.
Fixture recipe script documentation
tests/fixture_recipes/*_recipe.py, tests/fixture_recipes/regenerate_all.py
Module docstrings and regeneration pattern in fixture recipe scripts are updated to reference _recipe.py filenames instead of .recipe.py; no runtime logic changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • SETI/rms-picmaker#24: Extends the PDS3 pointer-resolution path that this PR threads the new pds3_label_method parameter through.
  • SETI/rms-picmaker#6: Previously modified the PDS3 label parsing flow and .lbl/.LBL fallback behavior that this PR now parameterizes with configurable strictness.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes both main changes: fixture recipe file renaming and the new --pds3-label-method option.
Description check ✅ Passed The PR description is comprehensive and detailed. While some template sections are not formally filled in, the narrative provides clear coverage of Purpose (the changes needed), Changes/Implementation Details (plumbing map and detailed file-by-file changes), Testing (test plan results), and Impact (backward compatibility and scope), making it substantively complete.
Docstring Coverage ✅ Passed Docstring coverage is 90.48% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/picmaker/io.py`:
- Around line 297-302: Validate the pds3_label_method argument at the public I/O
boundary in read_pds_labeled_image_array: check that pds3_label_method is a str
(raise TypeError if not) and that its value is one of the allowed options (e.g.,
'strict', 'lenient', etc.; raise ValueError for unknown values), and include a
clear message describing the valid choices; apply the same explicit type/value
validation to any sibling public read_pds_* functions that accept
pds3_label_method so callers get immediate, clear TypeError/ValueError rather
than downstream failures.

In `@tests/fixture_recipes/regenerate_all.py`:
- Line 2: The docstring currently mentions the pattern "sibling
`<name>_recipe.py` module." without a Sphinx cross-reference; update that
narrative to use a module role such as :mod:`<name>_recipe` so the line reads
something like "sibling :mod:`<name>_recipe` module." in the docstring of
tests/fixture_recipes/regenerate_all.py (locate the docstring containing that
phrase and replace the plain-text reference with the :mod: role).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: eb81ff7d-cbf7-4e4e-ad3a-cf4b47584b6f

📥 Commits

Reviewing files that changed from the base of the PR and between 26739ad and 4cfb15c.

📒 Files selected for processing (29)
  • docs/dev/adding_an_instrument.rst
  • docs/dev/pipeline.rst
  • docs/user_guide.rst
  • src/picmaker/cli.py
  • src/picmaker/io.py
  • src/picmaker/options.py
  • src/picmaker/pipeline.py
  • tests/conftest.py
  • tests/fixture_recipes/cassini_iss_recipe.py
  • tests/fixture_recipes/corrupt_fits_recipe.py
  • tests/fixture_recipes/corrupt_vicar_recipe.py
  • tests/fixture_recipes/galileo_ssi_a_recipe.py
  • tests/fixture_recipes/galileo_ssi_b_recipe.py
  • tests/fixture_recipes/hst_acs_recipe.py
  • tests/fixture_recipes/hst_wfc3_recipe.py
  • tests/fixture_recipes/hst_wfpc2_recipe.py
  • tests/fixture_recipes/malformed_numpy_recipe.py
  • tests/fixture_recipes/malformed_pickle_recipe.py
  • tests/fixture_recipes/nh_mvic_recipe.py
  • tests/fixture_recipes/pds3_sample_recipe.py
  • tests/fixture_recipes/regenerate_all.py
  • tests/fixture_recipes/small_grayscale_recipe.py
  • tests/fixture_recipes/small_rgb_recipe.py
  • tests/fixture_recipes/small_tiff16_recipe.py
  • tests/fixture_recipes/voyager_iss_recipe.py
  • tests/fixtures/.baseline-flags.txt
  • tests/test_cli.py
  • tests/test_cli_unit.py
  • tests/test_pipeline_helpers.py

Comment thread src/picmaker/io.py
Comment on lines 297 to 302
def read_pds_labeled_image_array(
filename: str | os.PathLike[str], obj: ObjectSelector = None
filename: str | os.PathLike[str],
obj: ObjectSelector = None,
*,
pds3_label_method: str = 'strict',
) -> ReadResult | None:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Validate pds3_label_method at the public I/O boundary.

read_pds_labeled_image_array accepts caller input and forwards it directly. Add explicit
type/value checks so direct library callers always get clear TypeError/ValueError
messages for invalid values.

🔧 Proposed fix
 from picmaker.pil_utils import array_to_pil, pil_to_array
+from picmaker.options import PDS3_LABEL_METHODS
 from picmaker.tiff16 import ReadTiff16
@@
 def read_pds_labeled_image_array(
@@
 ) -> ReadResult | None:
@@
+    if not isinstance(pds3_label_method, str):
+        raise TypeError(
+            f'pds3_label_method must be a str, got {type(pds3_label_method).__name__}'
+        )
+    if pds3_label_method not in PDS3_LABEL_METHODS:
+        raise ValueError(
+            f'invalid pds3_label_method {pds3_label_method!r}; '
+            f'must be one of {PDS3_LABEL_METHODS}'
+        )
+
     filename_str = str(filename)
As per coding guidelines, "NEVER trust external input (function arguments from callers...). Validate inputs at the public API boundary of the library. Raise clear `ValueError` or `TypeError` exceptions for invalid arguments."

Also applies to: 319-334

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/picmaker/io.py` around lines 297 - 302, Validate the pds3_label_method
argument at the public I/O boundary in read_pds_labeled_image_array: check that
pds3_label_method is a str (raise TypeError if not) and that its value is one of
the allowed options (e.g., 'strict', 'lenient', etc.; raise ValueError for
unknown values), and include a clear message describing the valid choices; apply
the same explicit type/value validation to any sibling public read_pds_*
functions that accept pds3_label_method so callers get immediate, clear
TypeError/ValueError rather than downstream failures.

@@ -1,5 +1,5 @@
"""Regenerate every binary test fixture by importing and running each
sibling `<name>.recipe.py` module.
sibling `<name>_recipe.py` module.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Sphinx module cross-reference in the docstring line.

The narrative text references a module pattern without a Sphinx role. Please format this as a module reference (for example, using :mod:) to satisfy docs conventions.

As per coding guidelines, "EVERY mention of a class, method, function, module, attribute, or data constant in narrative prose MUST use appropriate Sphinx cross-reference roles (:class:, :meth:, :func:, :mod:, :attr:, :data:)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/fixture_recipes/regenerate_all.py` at line 2, The docstring currently
mentions the pattern "sibling `<name>_recipe.py` module." without a Sphinx
cross-reference; update that narrative to use a module role such as
:mod:`<name>_recipe` so the line reads something like "sibling
:mod:`<name>_recipe` module." in the docstring of
tests/fixture_recipes/regenerate_all.py (locate the docstring containing that
phrase and replace the plain-text reference with the :mod: role).

@codecov
Copy link
Copy Markdown

codecov Bot commented May 14, 2026

Codecov Report

❌ Patch coverage is 85.71429% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.26%. Comparing base (26739ad) to head (4cfb15c).

Files with missing lines Patch % Lines
src/picmaker/io.py 66.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #25      +/-   ##
==========================================
+ Coverage   90.24%   90.26%   +0.02%     
==========================================
  Files          19       19              
  Lines        1886     1891       +5     
  Branches      419      420       +1     
==========================================
+ Hits         1702     1707       +5     
  Misses         96       96              
  Partials       88       88              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rfrenchseti rfrenchseti merged commit fd5180f into main May 14, 2026
12 of 13 checks passed
@rfrenchseti rfrenchseti deleted the rf_260514 branch May 14, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant