diff --git a/pytheory/tones.py b/pytheory/tones.py index 21a7fdb..1b26e2e 100644 --- a/pytheory/tones.py +++ b/pytheory/tones.py @@ -31,6 +31,7 @@ class Tone: alt_names: Optional[list[str]] = None, octave: Optional[int] = None, system: Union[str, object] = "western", + _validate: bool = True, ) -> None: """Initialize a Tone with a name, optional octave, and musical system. @@ -68,6 +69,13 @@ class Tone: self.system_name = None self._system = system + # Validate tone name against the system early (fixes #39). + if _validate and self.system.resolve_name(name) is None: + raise ValueError( + f"Unknown tone name: {name!r}. " + f"Not found in the {system!r} system." + ) + @property def exists(self) -> bool: """True if this tone's name is found in the associated system.""" @@ -345,7 +353,7 @@ class Tone: if system: return klass(name=tone, octave=octave, system=system) else: - return klass(name=tone, octave=octave) + return klass(name=tone, octave=octave, _validate=False) @classmethod def from_tuple(klass, t: tuple[str, ...]) -> Tone: diff --git a/test_pytheory.py b/test_pytheory.py index b83cc61..48ffd16 100644 --- a/test_pytheory.py +++ b/test_pytheory.py @@ -68,9 +68,16 @@ def test_tone_system(): def test_tone_exists(): c4 = Tone(name="C", octave=4, system="western") - invalid_tone = Tone(name="H", octave=4, system="western") assert c4.exists is True - assert invalid_tone.exists is False + + +def test_tone_invalid_raises(): + """Invalid tone names raise ValueError at construction time (fixes #39).""" + import pytest + with pytest.raises(ValueError, match="Unknown tone name"): + Tone(name="H", octave=4, system="western") + with pytest.raises(ValueError, match="Unknown tone name"): + Tone("X") def test_tone_names_method(): @@ -4839,10 +4846,11 @@ def test_solfege_no_octave(): assert t.solfege == "Do" -def test_solfege_unknown_returns_name(): - """A non-standard name should be returned unchanged.""" - t = Tone(name="X", system="western") - assert t.solfege == "X" +def test_solfege_unknown_raises(): + """A non-standard name should raise ValueError at construction (fixes #39).""" + import pytest + with pytest.raises(ValueError, match="Unknown tone name"): + Tone(name="X", system="western") # ── Rhythm / Duration system ────────────────────────────────────────────────