SoundCard
SoundCard copied to clipboard
Don't hide common classes / hide imports
If trying to create a higher level script with custom classes, it is helpful to have direct access to the common classes of SoundCard, e.g.:
import soundcard as sc
# ...
class AudioChannel(object):
def play(self, speaker):
assert isinstance(speaker, sc.Speaker), 'speaker must be a speaker'
# ...
```
Currently, those classes are hidden and not accessible. However, internals (imports) like cffi, re, os, ... should not be visible to the developer trying out that package using ipython or similar.
I don't quite understand your comment. What exactly are you trying to do, and what do you think SoundCard is doing wrong?
It isn't doing anything wrong, but if one wants to explore a module, it is helpful to hide imports that the developer does not need to see. I changed everything except numpy to start with underscores:
os
-> _os
time
-> _time
And I renamed _Speaker
, _Microphone
, _Player
and _Recorder
so that they are usable. Before,
import soundcard as sc
sc._Speaker
raised an AttributeError
: the module has no attribute _Speaker.
My fork here contains those changes and some linter eyecandy (visual indentation, some new docstrings) but I'm still working on it. I'll create a pull request when I'm happy with it.
There's a reason for the underscores on _Speaker
, _Microphone
, _Player
, and _Recorder
: They are not meant to be used by users. There is essentially no way for a user to supply them with their required arguments. That's why they are hidden.
As for renaming common imports, do you have a reference for this? I know this practice exists, but I find it profoundly ugly. Could you accomplish a similar goal by specifying an __all__
?
What exactly are you trying to do
One use case: I was trying to use type annotations for functions that passed around _Microphone
and _Speaker
objects but I couldn't because they're not exported.
Could you accomplish a similar goal by specifying an
__all__
?
Yes, __all__
can include identifiers with underscores; the name alone indicates that it's an internal identifier even if it's exported.
The main downside of __all__
is that it's extra work: you need to keep it up to date for every implementation (pulseaudio/coreaudio/mediafoundation) and then again in __init__.py
(although the code is identical between them all).
For reference, I've been using this:
__all__ = (
'_Microphone',
'_Player',
'_Recorder',
'_Speaker',
'all_microphones',
'all_speakers',
'default_microphone',
'default_speaker',
'get_microphone',
'get_speaker',
)
(those are the classes that appears to be common, but I've only tested on windows)
Yes, type annotations are a good reason for accessing these. But then, I think import soundcard._Microphone
is a sensible solution to this problem. It's only four imports, after all.
If anyone cares enough about this to contribute a pull request, I'll be happy to merge it.