Skip to content

Wave_write throws an error if a setter to an written header information is set to the same value #132445

Description

@ygerlach

Feature or enhancement

Proposal:

The Wave_write class does not allow to "reset" the same value. This makes some implementations quite complicated, as if you want to continuously append data to the wav file, you need to differentiate between first and the other writes.
Libraries, that use this class (for example piper-tts) are sometimes making it hard to do this right. Which is probably a bug in those libraries, but i think python should make it easier for them to not make this mistake.

import wave

with wave.open(open('out.wav', 'wb'), 'w') as w:
	w.setframerate(44100)
	w.setnchannels(1)
	w.setsampwidth(1)
	w.writeframes(b'0' * 44100)

	w.setframerate(44100)  # should not fail
	w.setnchannels(1)  # should not fail
	w.setsampwidth(1)  # should not fail

	w.setframerate(22050)  # should still fail
	w.setnchannels(2)  # should still fail
	w.setsampwidth(2)  # should still fail

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Linked PRs

Activity

added
stdlibStandard Library Python modules in the Lib/ directory
on Apr 12, 2025

picnixz commented on Apr 12, 2025

@picnixz
Member

I think we can make it a no-op if we use the same value, but I'm not familiar with this module. Checking for whether something can be silently ignored or not requires to call the getters whenever we call the setters. I don't think it'll become slower by much but, ideally, I'd think it's more of an API misuse.

That being said, it's also better if we don't make existing librairies unsable (piper-tts seems to have 30k downloads per month, not necessarily much but still noticable I'd say). Since the module is also quite niche on our side, I think we can do this kind of work on our side.

@serhiy-storchaka any thoughts on this one?

added a commit that references this issue on Apr 12, 2025

serhiy-storchaka commented on Apr 12, 2025

@serhiy-storchaka
Member

I do not think that this is a common case. Setters should be called after opening a file and before writing any data. If setter fails with some values and successes with other value, this can lead to bugs, when the code was tested with same values, but users use different values.

added a commit that references this issue on Apr 23, 2025

ygerlach commented on Apr 23, 2025

@ygerlach
Author

If setter fails with some values and successes with other value, this can lead to bugs

We already have this case. Imagine a function that writes wave frames to a wave object, after setting its parameters.

  • We pass a newly opened wave object: everything is fine
  • We pass another wave object (that - god forbid - was already written too) - we get an exception, despite calling it with the same values.
  • Even worse: we pass the same wave object multiple times: it works great the first time and raises an exception every other time. And that is exactly the problem / bug i ran into. Sure it is not that hard to throw a try/except around every one of your setters, but it is ugly and easy to miss something (like forgetting to check the getters for the correct values if it fails). See here, where i did that, to work around this "feature". A simple one line setter call grows into an ugly 5 line abomination.

requires to call the getters whenever we call the setters

That is not possible for all the getters in their current state, as they may also raise an Error. Also some values do not have a getter. That's why i opted to use those member variables directly.

added 2 commits that reference this issue on Apr 30, 2025

serhiy-storchaka commented on Feb 25, 2026

@serhiy-storchaka
Member

Look at this as an implementation of the Builder pattern. Before any data has been written, the Wave_write object is still in the stage of building. open() + set*() methods is a constructor.

If we did it now, it would be better to use more traditional idioms -- either pass framerate, nchannels, etc, as keyword arguments to constructor, or have a separate Builder or Factory class which allow to set parameters and then produce completely initialized Wave_write object. But what's done is done.

Consider a newly opened Wave_write object as partially initialized. All set*() methods should be called by the code that calls open(). The function that takes the Wave_write object should not call set*() methods.

The wave module is legacy. Its sister modules for other formats have been removed. We are not planning to add new features or new interface.

serhiy-storchaka commented on Jul 3, 2026

@serhiy-storchaka
Member

The constructor-phase behavior is by design, and wave is a legacy module which does not get new features. Closing as not planned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stdlibStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions