-
Notifications
You must be signed in to change notification settings - Fork 1.7k
program: add subcommand CLI support #2793
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
Conversation
Summary:
This commit adds a subcommand interface to support invocations in the
manner of `apt-get install` or `git commit`. All existing invocations
remain valid, and are now also available under the `tensorboard serve`
subcommand. Other subcommands may be injected programmatically. For
instance, we could migrate the existing `tensorboard --inspect` to
`tensorboard inspect`, which makes more sense semantically (as it does
something quite different from starting a web server).
To support both `tensorboard --flags` and `tensorboard serve --flags`,
we require some hacks first to get off the ground at all in Python 2,
and then to have sane error messages without massive flag duplication in
all Python versions. Even recent Python versions don’t have a notion of
a _default_ subcommand, so we need to manually inspect `argv` to
determine what kind of argument parser to construct.
Test Plan:
Unit tests added, and the smoke test for the dynamic plugin (plus the
Pip package test script) serve as integration tests for existing
behavior. It’s important to test this in both Python 2 and Python 3. For
manual testing:
- Check that the output of `tensorboard --help` still includes all the
normal flags, and now has a list of subcommands (one item long).
- Check that the output of `tensorboard serve --help` is just like the
output of `tensorboard --help`, but without the list of subcommands.
- Patch a `demo` subcommand into `main.py`, and check that the output
of `tensorboard demo --help` is just that subcommand’s help (without
flags to `serve`) and that `tensorboard --help` shows the `demo`
subcommand.
wchargin-branch: subcommands
Another idea: we could call this |
nfelt
left a comment
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! Let the subcommand proliferation begin :D
tensorboard/program.py
Outdated
| flags = parser.parse_args(argv[1:]) # Strip binary name from argv. | ||
|
|
||
| argparse_monkey_patch = _ArgparseMonkeyPatch() | ||
| argparse_monkey_patch.install() |
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.
Maybe just make this a context manager if we only need to install/uninstall in a context-manager-friendly context?
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.
Good idea; thanks! Done.
tensorboard/program.py
Outdated
| efi.inspect(flags.logdir, event_file, flags.tag) | ||
| return 0 | ||
| if self.flags.version_tb: | ||
| if flags.version_tb: |
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.
Can you add a TODO(#2801) here? The version flag really should be installed only on the base parser, not as a subflag of serve, but that requires moving it out of the plugin-centric flag definition system.
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.
Done.
tensorboard/program.py
Outdated
| logging.getLogger('werkzeug').setLevel(logging.NOTSET) | ||
|
|
||
|
|
||
| class _ArgparseMonkeyPatch(object): |
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.
Optional, but maybe nicer to put this in an argparse_util.py or something since it's otherwise not really relevant to program.py.
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.
Done.
tensorboard/program.py
Outdated
| class _ArgparseMonkeyPatch(object): | ||
| """Make Python 2.7 behave like Python 3 w.r.t. default subcommands. | ||
| The behavior of argparse was changed in Python 3.3. When a parser |
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.
Might as well add a reference to https://bugs.python.org/issue16308 and python/cpython@f97c59a for the curious?
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.
Meant to do this, yes; thanks!
wchargin-branch: subcommands wchargin-source: 1a3423e24da6b619baf06c26dbd3fbfdd473970a
tensorboard/util/argparse_util.py
Outdated
| return real_error(*args, **kwargs) | ||
|
|
||
| argparse.ArgumentParser.error = error | ||
| yield |
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.
We want a try-finally around the yield, right? As shown in https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager
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.
Agh, yes, of course. For some reason I thought that contextlib handled
that for us, but of course it can’t, because _handling_errors and
friends need to be able to respond to errors. Thanks; fixed.
| from tensorboard.util import argparse_util | ||
|
|
||
|
|
||
| class AllowMissingSubcommandTest(tb_test.TestCase): |
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.
Awesome, thanks for adding these tests too!
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.
Of course.
wchargin-branch: subcommands wchargin-source: b9448d785b8bfdcf458df60ed0ff0da991270f03
Summary:
This commit adds a subcommand interface to support invocations in the
manner of `apt-get install` or `git commit`. All existing invocations
remain valid, and are now also available under the `tensorboard serve`
subcommand. Other subcommands may be injected programmatically. For
instance, we could migrate the existing `tensorboard --inspect` to
`tensorboard inspect`, which makes more sense semantically (as it does
something quite different from starting a web server).
To support both `tensorboard --flags` and `tensorboard serve --flags`,
we require some hacks first to get off the ground at all in Python 2,
and then to have sane error messages without massive flag duplication in
all Python versions. Even recent Python versions don’t have a notion of
a _default_ subcommand, so we need to manually inspect `argv` to
determine what kind of argument parser to construct.
Test Plan:
Unit tests added, and the smoke test for the dynamic plugin (plus the
Pip package test script) serve as integration tests for existing
behavior. It’s important to test this in both Python 2 and Python 3. For
manual testing:
- Check that the output of `tensorboard --help` still includes all the
normal flags, and now has a list of subcommands (one item long).
- Check that the output of `tensorboard serve --help` is just like the
output of `tensorboard --help`, but without the list of subcommands.
- Patch a `demo` subcommand into `main.py`, and check that the output
of `tensorboard demo --help` is just that subcommand’s help (without
flags to `serve`) and that `tensorboard --help` shows the `demo`
subcommand.
Finally, note that changing the implementation of the monkey patch to
just `yield; return` causes both `argparse_util_test` and `program_test`
to fail on Python 2 (but not Python 3).
wchargin-branch: subcommands
Summary:
This commit adds a subcommand interface to support invocations in the
manner of `apt-get install` or `git commit`. All existing invocations
remain valid, and are now also available under the `tensorboard serve`
subcommand. Other subcommands may be injected programmatically. For
instance, we could migrate the existing `tensorboard --inspect` to
`tensorboard inspect`, which makes more sense semantically (as it does
something quite different from starting a web server).
To support both `tensorboard --flags` and `tensorboard serve --flags`,
we require some hacks first to get off the ground at all in Python 2,
and then to have sane error messages without massive flag duplication in
all Python versions. Even recent Python versions don’t have a notion of
a _default_ subcommand, so we need to manually inspect `argv` to
determine what kind of argument parser to construct.
Test Plan:
Unit tests added, and the smoke test for the dynamic plugin (plus the
Pip package test script) serve as integration tests for existing
behavior. It’s important to test this in both Python 2 and Python 3. For
manual testing:
- Check that the output of `tensorboard --help` still includes all the
normal flags, and now has a list of subcommands (one item long).
- Check that the output of `tensorboard serve --help` is just like the
output of `tensorboard --help`, but without the list of subcommands.
- Patch a `demo` subcommand into `main.py`, and check that the output
of `tensorboard demo --help` is just that subcommand’s help (without
flags to `serve`) and that `tensorboard --help` shows the `demo`
subcommand.
Finally, note that changing the implementation of the monkey patch to
just `yield; return` causes both `argparse_util_test` and `program_test`
to fail on Python 2 (but not Python 3).
wchargin-branch: subcommands
Summary: In #2793, we added a helper to ensure that you can run `tensorboard` either with or without an explicit subcommand. This works by default in Python 3; the helper was only needed for Python 2. Thus, we remove it. Part of #4488. Test Plan: The test plan from #2793 still passes. Instead of patching in a `demo` subcommand, you can now use `tensorboard dev list [--json]`. wchargin-branch: py3-no-argparseutil wchargin-source: 0f5517b1b09e4fae5158096d55cf8b6247b2e2e2
Summary: In #2793, we added a helper to ensure that you can run `tensorboard` either with or without an explicit subcommand. This works by default in Python 3; the helper was only needed for Python 2. Thus, we remove it. Part of #4488. Test Plan: The test plan from #2793 still passes. Instead of patching in a `demo` subcommand, you can now use `tensorboard dev list [--json]`. wchargin-branch: py3-no-argparseutil
Summary:
This commit adds a subcommand interface to support invocations in the
manner of
apt-get installorgit commit. All existing invocationsremain valid, and are now also available under the
tensorboard servesubcommand. Other subcommands may be injected programmatically. For
instance, we could migrate the existing
tensorboard --inspecttotensorboard inspect, which makes more sense semantically (as it doessomething quite different from starting a web server).
To support both
tensorboard --flagsandtensorboard serve --flags,we require some hacks first to get off the ground at all in Python 2,
and then to have sane error messages without massive flag duplication in
all Python versions. Even recent Python versions don’t have a notion of
a default subcommand, so we need to manually inspect
argvtodetermine what kind of argument parser to construct.
Test Plan:
Unit tests added, and the smoke test for the dynamic plugin (plus the
Pip package test script) serve as integration tests for existing
behavior. It’s important to test this in both Python 2 and Python 3. For
manual testing:
tensorboard --helpstill includes all thenormal flags, and now has a list of subcommands (one item long).
tensorboard serve --helpis just like theoutput of
tensorboard --help, but without the list of subcommands.demosubcommand intomain.py, and check that the outputof
tensorboard demo --helpis just that subcommand’s help (withoutflags to
serve) and thattensorboard --helpshows thedemosubcommand.
Finally, note that changing the implementation of the monkey patch to
just
yield; returncauses bothargparse_util_testandprogram_testto fail on Python 2 (but not Python 3).
wchargin-branch: subcommands