Skip to content

Commit ac5b43c

Browse files
authored
data: expose data_location integration point (#2627)
Summary: Generic data providers can now control the “data location” string in the TensorBoard UI, which appears at the bottom of the runs selector: ![Screenshot of the runs selector and its data location string][ss] As this is only a cosmetic change, it’s provided as an optional method on the `DataProvider` interface. [ss]: https://user-images.githubusercontent.com/4317806/64572837-cf7b7e00-d31d-11e9-8fd6-d81d36774172.png Test Plan: TensorBoard still seems to work properly with `--generic_data` set to `false`, `auto`, or `true`, in both logdir and DB modes. Conspicuously changing the `MultiplexerDataProvider` to always `return "tada"` induces the appropriate change in the frontend, so the wiring is hooked up. wchargin-branch: generic-data-location
1 parent 2ff9cf0 commit ac5b43c

File tree

9 files changed

+53
-11
lines changed

9 files changed

+53
-11
lines changed

tensorboard/backend/application.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,9 @@ def standard_tensorboard_wsgi(flags, plugin_loaders, assets_zip_provider):
138138
max_reload_threads=flags.max_reload_threads,
139139
event_file_active_filter=_get_event_file_active_filter(flags))
140140
if flags.generic_data != 'false':
141-
data_provider = event_data_provider.MultiplexerDataProvider(multiplexer)
141+
data_provider = event_data_provider.MultiplexerDataProvider(
142+
multiplexer, flags.logdir
143+
)
142144

143145
if reload_interval >= 0:
144146
# We either reload the multiplexer once when TensorBoard starts up, or we

tensorboard/backend/event_processing/data_provider.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,17 @@
2929

3030

3131
class MultiplexerDataProvider(provider.DataProvider):
32-
def __init__(self, multiplexer):
32+
def __init__(self, multiplexer, logdir):
3333
"""Trivial initializer.
3434
3535
Args:
3636
multiplexer: A `plugin_event_multiplexer.EventMultiplexer` (note:
3737
not a boring old `event_multiplexer.EventMultiplexer`).
38+
logdir: The log directory from which data is being read. Only used
39+
cosmetically. Should be a `str`.
3840
"""
3941
self._multiplexer = multiplexer
42+
self._logdir = logdir
4043

4144
def _test_run_tag(self, run_tag_filter, run, tag):
4245
runs = run_tag_filter.runs
@@ -53,6 +56,10 @@ def _get_first_event_timestamp(self, run_name):
5356
except ValueError as e:
5457
return None
5558

59+
def data_location(self, experiment_id):
60+
del experiment_id # ignored
61+
return str(self._logdir)
62+
5663
def list_runs(self, experiment_id):
5764
del experiment_id # ignored for now
5865
return [

tensorboard/backend/event_processing/data_provider_test.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,13 @@ def create_multiplexer(self):
7575
return multiplexer
7676

7777
def create_provider(self):
78-
return data_provider.MultiplexerDataProvider(self.create_multiplexer())
78+
multiplexer = self.create_multiplexer()
79+
return data_provider.MultiplexerDataProvider(multiplexer, self.logdir)
80+
81+
def test_data_location(self):
82+
provider = self.create_provider()
83+
result = provider.data_location(experiment_id="unused")
84+
self.assertEqual(result, self.logdir)
7985

8086
def test_list_runs(self):
8187
# We can't control the timestamps of events written to disk (without
@@ -102,7 +108,7 @@ def FirstEventTimestamp(multiplexer, run):
102108
return result
103109

104110
multiplexer = FakeMultiplexer()
105-
provider = data_provider.MultiplexerDataProvider(multiplexer)
111+
provider = data_provider.MultiplexerDataProvider(multiplexer, "fake_logdir")
106112
result = provider.list_runs(experiment_id="unused")
107113
self.assertItemsEqual(result, [
108114
base_provider.Run(run_id=run, run_name=run, start_time=start_time)
@@ -164,7 +170,7 @@ def test_list_scalars_filters(self):
164170

165171
def test_read_scalars(self):
166172
multiplexer = self.create_multiplexer()
167-
provider = data_provider.MultiplexerDataProvider(multiplexer)
173+
provider = data_provider.MultiplexerDataProvider(multiplexer, self.logdir)
168174

169175
run_tag_filter = base_provider.RunTagFilter(
170176
runs=["waves", "polynomials", "unicorns"],

tensorboard/components/tf_backend/environmentStore.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ namespace tf_backend {
2222
private environment: Environment;
2323

2424
load() {
25-
const url = tf_backend.getRouter().environment();
25+
const url = tf_backend
26+
.getRouter()
27+
.environment(tf_backend.getExperimentId());
2628
return this.requestManager.request(url).then((result) => {
2729
const environment = {
2830
dataLocation: result.data_location,

tensorboard/components/tf_backend/router.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ limitations under the License.
1414
==============================================================================*/
1515
namespace tf_backend {
1616
export interface Router {
17-
environment: () => string;
17+
environment: (experiment?: string) => string;
1818
experiments: () => string;
1919
pluginRoute: (
2020
pluginName: string,
@@ -39,7 +39,11 @@ namespace tf_backend {
3939
dataDir = dataDir.slice(0, dataDir.length - 1);
4040
}
4141
return {
42-
environment: () => createDataPath(dataDir, '/environment'),
42+
environment: (experiment?: string) => {
43+
const searchParams = new URLSearchParams();
44+
searchParams.set('experiment', experiment || '');
45+
return createDataPath(dataDir, '/environment', searchParams);
46+
},
4347
experiments: () => createDataPath(dataDir, '/experiments'),
4448
pluginRoute: (
4549
pluginName: string,

tensorboard/components/tf_backend/test/backendTests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ namespace tf_backend {
9797
});
9898

9999
it('returns correct value for #environment', () => {
100-
assert.equal(router.environment(), 'data/environment');
100+
assert.equal(router.environment(), 'data/environment?experiment=');
101101
});
102102

103103
it('returns correct value for #experiments', () => {

tensorboard/data/provider.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ class DataProvider(object):
3333
downsampling strategies or domain restriction by step or wall time.
3434
"""
3535

36+
def data_location(self, experiment_id):
37+
"""Render a human-readable description of the data source.
38+
39+
For instance, this might return a path to a directory on disk.
40+
41+
The default implementation always returns the empty string.
42+
43+
Args:
44+
experiment_id: ID of enclosing experiment.
45+
46+
Returns:
47+
A string, which may be empty.
48+
"""
49+
return ""
50+
51+
3652
@abc.abstractmethod
3753
def list_runs(self, experiment_id):
3854
"""List all runs within an experiment.

tensorboard/plugins/core/core_plugin.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,15 @@ def _serve_environment(self, request):
120120
database (depending on which mode TensorBoard is running in).
121121
* window_title is the title of the TensorBoard web page.
122122
"""
123+
if self._data_provider:
124+
experiment = request.args.get('experiment', '')
125+
data_location = self._data_provider.data_location(experiment)
126+
else:
127+
data_location = self._logdir or self._db_uri
123128
return http_util.Respond(
124129
request,
125130
{
126-
'data_location': self._logdir or self._db_uri,
131+
'data_location': data_location,
127132
'window_title': self._window_title,
128133
},
129134
'application/json')

tensorboard/plugins/scalar/scalars_plugin_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def wrapper(self, *args, **kwargs):
124124
fn(self, scalars_plugin.ScalarsPlugin(ctx), *args, **kwargs)
125125
with self.subTest('generic data provider'):
126126
flags = argparse.Namespace(generic_data='true')
127-
provider = data_provider.MultiplexerDataProvider(multiplexer)
127+
provider = data_provider.MultiplexerDataProvider(multiplexer, logdir)
128128
ctx = base_plugin.TBContext(
129129
flags=flags,
130130
logdir=logdir,

0 commit comments

Comments
 (0)