-
Notifications
You must be signed in to change notification settings - Fork 664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor backend and not rely on global variables on switching #698
Conversation
8b2acce
to
49ca6a2
Compare
Codecov Report
@@ Coverage Diff @@
## master #698 +/- ##
==========================================
+ Coverage 89.03% 89.05% +0.01%
==========================================
Files 27 28 +1
Lines 2417 2467 +50
==========================================
+ Hits 2152 2197 +45
- Misses 265 270 +5
Continue to review full report at Codecov.
|
60b0fee
to
e2643cd
Compare
6f6ff0f
to
d84a53e
Compare
1. Do not rely on global variables for backend switch So that load/save/info/load_wav functions will be torchscript-able 2. Add no_backend module to for the case there is no backend module available [bonus] This allows the whole codebase importable on systems that do not have torchaudio C++ extension nor soundfile.
@@ -29,11 +29,13 @@ def test_librispeech(self): | |||
data[0] | |||
|
|||
@unittest.skipIf("sox" not in common_utils.BACKENDS, "sox not available") | |||
@common_utils.AudioBackendScope('sox') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #715
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this fixes #715 completely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. This is a temporary fix. #719 gives the principled fix which fully utilizes no_backend
from this PR.
def _impl_load(func): | ||
setattr(func, '__doc__', _LOAD_DOCSTRING) | ||
return func | ||
|
||
|
||
def _impl_load_wav(func): | ||
setattr(func, '__doc__', _LOAD_WAV_DOCSTRING) | ||
return func | ||
|
||
|
||
def _impl_save(func): | ||
setattr(func, '__doc__', _SAVE_DOCSTRING) | ||
return func | ||
|
||
|
||
def _impl_info(func): | ||
setattr(func, '__doc__', _INFO_DOCSTRING) | ||
return func |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: how about calling them _add_docstring_*
instead?
Instead of having these functions, we could also use "no backend" as the template to grab the docstring from.
raise RuntimeError('Backend is not initialized.') | ||
return _BACKENDS[_BACKEND] | ||
for func in ['save', 'load', 'load_wav', 'info']: | ||
setattr(torchaudio, func, getattr(module, func)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just make sure we make that conscious choice: torchaudio.backend.__init__
invokes utils._init_audio_backend()
which then modifies torchaudio
.
Would it be advantageous to attach load
, save
, etc directly in torchaudio.__init__
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure what you mean by advantageous
here, but the current approach gives cleaner separation of torchaudio.__init__
and torchaudio.backend
because backend
module provides 1. backend implementations, 2. way to manipulate backend, and 3. way to fetch information about current backend and available backends. This way, all the responsibilities on backend is closed within torchaudio.backend
module and the only contract between torchaudio.__init__
and torchaudio.backend
is that torchaudio.load/save/load_wav/info
attributes are reserved for torchaudio.backend
module (implying that as long as torchaudio.__init__
does not touch these attributes, torchaudio.backend
understands everything about it), and these attributes are the only attributes outside of torchaudio.backend
that torchaudio.backend
module is allowed to touch.
If the initialization is moved to torchaudio.__init__
, that means there are more contract between torchaudio.__init__
and torchaudio.backend
, like how the backend switch function works, and it requires torchaudio.__init__
to know the internal mechanics of torchaudio.backend
. This introduces more coupling between torchaudio.__init__
and torchaudio.backend
and adds maintenance cost.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main concern was the type of coupling that led to #712, when one module modifies another.
If I understand what you are saying: in order to avoid having the two depending on each other, we would need to bring torchaudio.backend
completely within torchaudio/__init__.py
. Is that what you are saying?
btw I am not advocating this :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main concern was the type of coupling that led to #712, when one module modifies another.
That's different. #712 was about new classes being defined inside of a module when a module function called from outside call.
Refactoring of backend switch is about achieving the proper Separation on Concerns, by letting the backend
module handle everything about backend, including initialization, so that main torchaudio
module does not need to.
If I understand what you are saying: in order to avoid having the two depending on each other, we would need to bring
torchaudio.backend
completely withintorchaudio/__init__.py
. Is that what you are saying?
No, that's totally opposite of what I am suggesting. It's better maitainable if torchaudio/__init__.py
knows nothing about backend
internal mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me rephrase my concern. If I read __init__
, I don't have any way of knowing that load/save/etc
are being added to it since it's added by another module. Am I missing something?
How about setting the attribute to torchaudio.backend
and simply importing in torchaudio/__init__.py
? Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me rephrase my concern. If I read
__init__
, I don't have any way of knowing thatload/save/etc
are being added to it since it's added by another module. Am I missing something?
Yes, that's the correct way to achieve the Separate of Concerns.
How about setting the attribute to
torchaudio.backend
and simply importing intorchaudio/__init__.py
? Thoughts?
No that way, the responsibility of backend switching is spread to torchaudio/__init__.py
and torchaudio.backend
, which introcudes more coupling. That's anti-pattern.
Just adding some comment about save/load/info
assigned in torchaudio.backend
would suffice.
@@ -29,11 +29,13 @@ def test_librispeech(self): | |||
data[0] | |||
|
|||
@unittest.skipIf("sox" not in common_utils.BACKENDS, "sox not available") | |||
@common_utils.AudioBackendScope('sox') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this fixes #715 completely?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
thanks! |
So that load/save/info/load_wav functions will be torchscript-able
[bonus] This allows the whole codebase importable on systems that do not have torchaudio C++ extension nor soundfile.