Skip to content

Add AlphaMode enum for stringly-typed alpha_mode field#705

Open
waltsims wants to merge 2 commits intomasterfrom
cleanup-alpha-mode-enum
Open

Add AlphaMode enum for stringly-typed alpha_mode field#705
waltsims wants to merge 2 commits intomasterfrom
cleanup-alpha-mode-enum

Conversation

@waltsims
Copy link
Copy Markdown
Owner

@waltsims waltsims commented Apr 1, 2026

  • AlphaMode(str, Enum) with NO_ABSORPTION, NO_DISPERSION, STOKES
  • kWaveMedium.post_init normalizes string inputs to enum
  • Backward compatible: AlphaMode == "no_dispersion" works via str inheritance
  • str returns the value so f-strings render cleanly

Greptile Summary

This PR introduces AlphaMode(str, Enum) to replace the stringly-typed alpha_mode field on kWaveMedium, providing IDE discoverability and a cleaner API while preserving backward compatibility through str inheritance.

Key changes:

  • New AlphaMode enum (NO_ABSORPTION, NO_DISPERSION, STOKES) with __str__ returning the raw value, so all existing == "no_dispersion" comparisons throughout the codebase (kWaveSimulation.py, create_absorption_variables.py, checks.py) continue to work correctly.
  • kWaveMedium.__post_init__ normalises plain-string inputs to AlphaMode at construction time.
  • check_fields updated to use isinstance(AlphaMode) instead of an in [...] list check.

Issues found:

  • The check_fields guard (isinstance(AlphaMode)) will incorrectly reject a valid plain string that was assigned to alpha_mode after construction (e.g. medium.alpha_mode = "no_dispersion"), because __post_init__ does not run again. This is a regression from the previous behaviour since the type hint explicitly advertises Union[AlphaMode, str].
  • Invalid strings passed at construction time raise a bare ValueError from the AlphaMode() constructor rather than the more informative message describing valid values.

Confidence Score: 4/5

  • Safe to merge after addressing the post-construction string regression in check_fields.
  • One P1 issue exists: valid string assignments made after object construction will be rejected by check_fields, breaking a previously-working usage pattern that the type hint still advertises as valid. All downstream string comparisons work correctly thanks to str inheritance, and construction-time string inputs are handled cleanly.
  • kwave/kmedium.py — specifically the check_fields validation logic at lines 61-65.

Important Files Changed

Filename Overview
kwave/enums.py Adds AlphaMode(str, Enum) with NO_ABSORPTION, NO_DISPERSION, STOKES values and a str returning the value — clean, minimal, no issues.
kwave/kmedium.py Normalises alpha_mode strings to AlphaMode in post_init, but check_fields still rejects valid plain strings on post-construction assignment, introducing a regression for mutated dataclass instances.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["kWaveMedium(alpha_mode=value)"] --> B{type of value?}
    B -->|"AlphaMode enum"| C[Store as-is]
    B -->|"str (plain)"| D["__post_init__: AlphaMode(value)"]
    D -->|valid string| E[Stored as AlphaMode]
    D -->|invalid string| F["ValueError from enum constructor\n(less descriptive message)"]
    B -->|None| G[Store None]
    B -->|post-construction assignment\nmedium.alpha_mode = 'str'| H["Bypasses __post_init__\n(plain str stored)"]
    H --> I{check_fields called?}
    I -->|Yes| J["isinstance check fails →\nValueError raised\n⚠️ REGRESSION"]
    I -->|No| K["Works: str comparisons pass\nvia str inheritance"]
    E --> L{check_fields called?}
    G --> L
    L -->|isinstance OK| M[Validation passes]
Loading

Reviews (1): Last reviewed commit: "Merge branch 'master' into cleanup-alpha..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

waltsims and others added 2 commits April 1, 2026 01:36
- AlphaMode(str, Enum) with NO_ABSORPTION, NO_DISPERSION, STOKES
- kWaveMedium.__post_init__ normalizes string inputs to enum
- Backward compatible: AlphaMode == "no_dispersion" works via str inheritance
- __str__ returns the value so f-strings render cleanly

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment on lines +61 to +65
if self.alpha_mode is not None and not isinstance(self.alpha_mode, AlphaMode):
raise ValueError(
f"medium.alpha_mode must be an AlphaMode enum value or one of "
f"'no_absorption', 'no_dispersion', 'stokes', got {self.alpha_mode!r}"
)
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.

P1 Post-construction string assignment silently breaks check_fields

check_fields is called from kWaveSimulation after the object is fully constructed (line 610 in kWaveSimulation.py). Since kWaveMedium is a plain dataclass, its attributes are mutable — a caller can legally write medium.alpha_mode = "no_dispersion" after __post_init__ runs, bypassing the normalization step. Under the old code that assignment would still pass the in [...] check; under this PR it raises a ValueError even for a valid string value, silently breaking previously-working patterns.

The type hint Optional[Union[AlphaMode, str]] reinforces the expectation that plain strings remain valid inputs throughout the object's lifetime. Fix by normalising strings in check_fields as well, or by converting alpha_mode to a property setter so normalisation is always applied on write:

# check the absorption mode input is valid (already normalized to AlphaMode in __post_init__)
if self.alpha_mode is not None:
    if isinstance(self.alpha_mode, str) and not isinstance(self.alpha_mode, AlphaMode):
        self.alpha_mode = AlphaMode(self.alpha_mode)  # late-normalise post-construction strings
    elif not isinstance(self.alpha_mode, AlphaMode):
        raise ValueError(
            f"medium.alpha_mode must be an AlphaMode enum value or one of "
            f"'no_absorption', 'no_dispersion', 'stokes', got {self.alpha_mode!r}"
        )

Comment on lines +47 to +48
if isinstance(self.alpha_mode, str) and not isinstance(self.alpha_mode, AlphaMode):
self.alpha_mode = AlphaMode(self.alpha_mode)
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.

P2 Invalid string raises unhelpful ValueError from enum constructor

When an invalid string is passed at construction time (e.g. kWaveMedium(sound_speed=1500, alpha_mode="typo")), AlphaMode("typo") raises ValueError: 'typo' is not a valid AlphaMode — the more descriptive message in check_fields is never reached. Consider wrapping the conversion to re-raise with a clearer hint:

if isinstance(self.alpha_mode, str) and not isinstance(self.alpha_mode, AlphaMode):
    try:
        self.alpha_mode = AlphaMode(self.alpha_mode)
    except ValueError:
        valid = [m.value for m in AlphaMode]
        raise ValueError(
            f"medium.alpha_mode must be one of {valid}, got {self.alpha_mode!r}"
        )

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 1, 2026

Codecov Report

❌ Patch coverage is 76.92308% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.42%. Comparing base (ba553eb) to head (ef65628).

Files with missing lines Patch % Lines
kwave/kmedium.py 71.42% 1 Missing and 1 partial ⚠️
kwave/enums.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #705      +/-   ##
==========================================
+ Coverage   74.40%   74.42%   +0.01%     
==========================================
  Files          56       56              
  Lines        8026     8035       +9     
  Branches     1570     1571       +1     
==========================================
+ Hits         5972     5980       +8     
- Misses       1437     1438       +1     
  Partials      617      617              
Flag Coverage Δ
3.10 74.42% <76.92%> (+0.01%) ⬆️
3.11 74.42% <76.92%> (+0.01%) ⬆️
3.12 74.42% <76.92%> (+0.01%) ⬆️
3.13 74.42% <76.92%> (+0.01%) ⬆️
macos-latest 74.39% <76.92%> (+0.01%) ⬆️
ubuntu-latest 74.39% <76.92%> (+0.01%) ⬆️
windows-latest 74.41% <76.92%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

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