SoundCard icon indicating copy to clipboard operation
SoundCard copied to clipboard

Don't hide common classes / hide imports

Open ep12 opened this issue 6 years ago • 7 comments

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.

ep12 avatar Aug 05 '18 13:08 ep12

I don't quite understand your comment. What exactly are you trying to do, and what do you think SoundCard is doing wrong?

bastibe avatar Aug 05 '18 15:08 bastibe

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.

ep12 avatar Aug 06 '18 16:08 ep12

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__?

bastibe avatar Aug 07 '18 10:08 bastibe

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

levic avatar Oct 10 '19 12:10 levic

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)

levic avatar Oct 10 '19 12:10 levic

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.

bastibe avatar Oct 14 '19 07:10 bastibe

If anyone cares enough about this to contribute a pull request, I'll be happy to merge it.

bastibe avatar Apr 28 '20 06:04 bastibe