Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tensorboard/backend/event_processing/data_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ def __init__(self, multiplexer, logdir):
self._multiplexer = multiplexer
self._logdir = logdir

def __str__(self):
return "MultiplexerDataProvider(logdir=%r)" % self._logdir

def _validate_context(self, ctx):
if type(ctx).__name__ != "RequestContext":
raise TypeError("ctx must be a RequestContext; got: %r" % (ctx,))
Expand Down
3 changes: 3 additions & 0 deletions tensorboard/data/grpc_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ def __init__(self, addr, stub):
self._addr = addr
self._stub = stub

def __str__(self):
return "GrpcDataProvider(addr=%r)" % self._addr

def data_location(self, ctx, *, experiment_id):
req = data_provider_pb2.GetExperimentRequest()
req.experiment_id = experiment_id
Expand Down
2 changes: 1 addition & 1 deletion tensorboard/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class ExperimentalNpmiPlugin(
# Ordering matters. The order in which these lines appear determines the
# ordering of tabs in TensorBoard's GUI.
_PLUGINS = [
core_plugin.CorePluginLoader,
core_plugin.CorePluginLoader(include_debug_info=True),
scalars_plugin.ScalarsPlugin,
custom_scalars_plugin.CustomScalarsPlugin,
images_plugin.ImagesPlugin,
Expand Down
10 changes: 10 additions & 0 deletions tensorboard/http_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,22 @@ Example response:

Returns environment in which the TensorBoard app is running.

The `version` is the Pip version string of the TensorBoard server, like
`"2.4.0a0"`.

The `window_title` is the value of the `--window_title` flag.

The `data_location` is a user-readable string describing the source from which
TensorBoard is reading data, such as a directory on disk.

The response may also include a `debug` field, with information that may be
useful for humans inspecting the system. The contents and structure of `debug`
are subject to change.

Example response:

{
"version": "2.4.0",
"window_title": "Custom Name",
"data_location": "/Users/tbuser/tensorboard_data/"
}
Expand Down
1 change: 1 addition & 0 deletions tensorboard/plugins/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ py_library(
srcs_version = "PY3",
deps = [
"//tensorboard:plugin_util",
"//tensorboard:version",
"//tensorboard/backend:http_util",
"//tensorboard/plugins:base_plugin",
"//tensorboard/util:grpc_util",
Expand Down
48 changes: 39 additions & 9 deletions tensorboard/plugins/core/core_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from tensorboard.plugins import base_plugin
from tensorboard.util import grpc_util
from tensorboard.util import tb_logging
from tensorboard import version

logger = tb_logging.get_logger()

Expand All @@ -50,18 +51,24 @@ class CorePlugin(base_plugin.TBPlugin):

plugin_name = "core"

def __init__(self, context):
def __init__(self, context, include_debug_info=None):
"""Instantiates CorePlugin.

Args:
context: A base_plugin.TBContext instance.
include_debug_info: If true, `/data/environment` will include some
basic information like the TensorBoard server version. Disabled by
default to prevent surprising information leaks in custom builds of
TensorBoard.
"""
self._flags = context.flags
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apropos of #2801 and comments on #4411: I don’t mind this because it
really does want to get all of flags rather than just using it as a
shortcut for some specific properties, and also because this
functionality really could be part of the actual core, anyway.
But open to pushback if you disagree.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree it's fine for now, although if you don't mind editing #2801 after this goes in so it reflects the current state that'd be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

logdir_spec = context.flags.logdir_spec if context.flags else ""
self._logdir = context.logdir or logdir_spec
self._window_title = context.window_title
self._path_prefix = context.flags.path_prefix if context.flags else None
self._assets_zip_provider = context.assets_zip_provider
self._data_provider = context.data_provider
self._include_debug_info = bool(include_debug_info)

def is_active(self):
return True
Expand Down Expand Up @@ -165,13 +172,7 @@ def _serve_index(self, index_asset_bytes, request):

@wrappers.Request.application
def _serve_environment(self, request):
"""Serve a JSON object containing some base properties used by the
frontend.

* data_location is either a path to a directory or an address to a
database (depending on which mode TensorBoard is running in).
* window_title is the title of the TensorBoard web page.
"""
"""Serve a JSON object describing the TensorBoard parameters."""
ctx = plugin_util.context(request.environ)
experiment = plugin_util.experiment_id(request.environ)
data_location = self._data_provider.data_location(
Expand All @@ -182,6 +183,7 @@ def _serve_environment(self, request):
)

environment = {
"version": version.VERSION,
"data_location": data_location,
"window_title": self._window_title,
}
Expand All @@ -193,12 +195,37 @@ def _serve_environment(self, request):
"creation_time": experiment_metadata.creation_time,
}
)
if self._include_debug_info:
environment["debug"] = {
"data_provider": str(self._data_provider),
"flags": self._render_flags(),
}
return http_util.Respond(
request,
environment,
"application/json",
)

def _render_flags(self):
"""Return a JSON-and-human-friendly version of `self._flags`.

Like `json.loads(json.dumps(self._flags, default=str))` but
without the wasteful serialization overhead.
"""
if self._flags is None:
return None

def go(x):
if isinstance(x, (type(None), str, int, float)):
return x
if isinstance(x, (list, tuple)):
return [go(v) for v in x]
if isinstance(x, dict):
return {str(k): go(v) for (k, v) in x.items()}
return str(x)

return go(vars(self._flags))

@wrappers.Request.application
def _serve_logdir(self, request):
"""Respond with a JSON object containing this TensorBoard's logdir."""
Expand Down Expand Up @@ -270,6 +297,9 @@ def _serve_experiment_runs(self, request):
class CorePluginLoader(base_plugin.TBLoader):
"""CorePlugin factory."""

def __init__(self, include_debug_info=None):
self._include_debug_info = include_debug_info

def define_flags(self, parser):
"""Adds standard TensorBoard CLI flags to parser."""
parser.add_argument(
Expand Down Expand Up @@ -640,7 +670,7 @@ def fix_flags(self, flags):

def load(self, context):
"""Creates CorePlugin instance."""
return CorePlugin(context)
return CorePlugin(context, include_debug_info=self._include_debug_info)


def _gzip(bytestring):
Expand Down
21 changes: 21 additions & 0 deletions tensorboard/plugins/core/core_plugin_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,27 @@ def testEnvironmentForLogdir(self):
parsed_object = self._get_json(self.server, "/data/environment")
self.assertEqual(parsed_object["data_location"], self.get_temp_dir())

def testEnvironmentDebugOffByDefault(self):
parsed_object = self._get_json(self.server, "/data/environment")
self.assertNotIn("debug", parsed_object)

def testEnvironmentDebugOnExplicitly(self):
multiplexer = event_multiplexer.EventMultiplexer()
logdir = self.get_temp_dir()
provider = data_provider.MultiplexerDataProvider(multiplexer, logdir)
context = base_plugin.TBContext(
assets_zip_provider=get_test_assets_zip_provider(),
logdir=logdir,
data_provider=provider,
window_title="title foo",
)
plugin = core_plugin.CorePlugin(context, include_debug_info=True)
app = application.TensorBoardWSGI([plugin])
server = werkzeug_test.Client(app, wrappers.BaseResponse)

parsed_object = self._get_json(server, "/data/environment")
self.assertIn("debug", parsed_object)

def testLogdir(self):
"""Test the format of the data/logdir endpoint."""
parsed_object = self._get_json(self.server, "/data/logdir")
Expand Down