Skip to content

Commit f06f40b

Browse files
authored
Refactor away TensorBoardWSGIApp in its current form (#2575)
1 parent 6996982 commit f06f40b

File tree

10 files changed

+76
-182
lines changed

10 files changed

+76
-182
lines changed

tensorboard/backend/application.py

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -157,42 +157,14 @@ def standard_tensorboard_wsgi(flags, plugin_loaders, assets_zip_provider):
157157
continue
158158
plugins.append(plugin)
159159
plugin_name_to_instance[plugin.plugin_name] = plugin
160-
return TensorBoardWSGIApp(flags.logdir, plugins, loading_multiplexer,
161-
reload_interval, flags.path_prefix,
162-
flags.reload_task)
163160

164-
165-
def TensorBoardWSGIApp(logdir, plugins, multiplexer, reload_interval,
166-
path_prefix='', reload_task='auto'):
167-
"""Constructs the TensorBoard application.
168-
169-
Args:
170-
logdir: the logdir spec that describes where data will be loaded.
171-
may be a directory, or comma,separated list of directories, or colons
172-
can be used to provide named directories
173-
plugins: A list of base_plugin.TBPlugin subclass instances.
174-
multiplexer: The EventMultiplexer with TensorBoard data to serve
175-
reload_interval: How often (in seconds) to reload the Multiplexer.
176-
Zero means reload just once at startup; negative means never load.
177-
path_prefix: A prefix of the path when app isn't served from root.
178-
reload_task: Indicates the type of background task to reload with.
179-
180-
Returns:
181-
A WSGI application that implements the TensorBoard backend.
182-
183-
Raises:
184-
ValueError: If something is wrong with the plugin configuration.
185-
186-
:type plugins: list[base_plugin.TBPlugin]
187-
:rtype: TensorBoardWSGI
188-
"""
189-
path_to_run = parse_event_files_spec(logdir)
190161
if reload_interval >= 0:
191162
# We either reload the multiplexer once when TensorBoard starts up, or we
192163
# continuously reload the multiplexer.
193-
start_reloading_multiplexer(multiplexer, path_to_run, reload_interval,
194-
reload_task)
195-
return TensorBoardWSGI(plugins, path_prefix)
164+
path_to_run = parse_event_files_spec(flags.logdir)
165+
start_reloading_multiplexer(
166+
loading_multiplexer, path_to_run, reload_interval, flags.reload_task)
167+
return TensorBoardWSGI(plugins, flags.path_prefix)
196168

197169

198170
class TensorBoardWSGI(object):

tensorboard/backend/application_test.py

Lines changed: 50 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ class FakePlugin(base_plugin.TBPlugin):
8181
"""A plugin with no functionality."""
8282

8383
def __init__(self,
84-
context,
85-
plugin_name,
86-
is_active_value,
87-
routes_mapping,
84+
context=None,
85+
plugin_name='foo',
86+
is_active_value=True,
87+
routes_mapping={},
8888
element_name_value=None,
8989
es_module_path_value=None,
9090
construction_callback=None):
@@ -138,19 +138,14 @@ def frontend_metadata(self):
138138
class ApplicationTest(tb_test.TestCase):
139139
def setUp(self):
140140
plugins = [
141+
FakePlugin(plugin_name='foo'),
141142
FakePlugin(
142-
None, plugin_name='foo', is_active_value=True, routes_mapping={}),
143-
FakePlugin(
144-
None,
145143
plugin_name='bar',
146144
is_active_value=False,
147-
routes_mapping={},
148145
element_name_value='tf-bar-dashboard',
149146
),
150147
FakePlugin(
151-
None,
152148
plugin_name='baz',
153-
is_active_value=True,
154149
routes_mapping={
155150
'/esmodule': lambda req: None,
156151
},
@@ -216,19 +211,14 @@ class ApplicationBaseUrlTest(tb_test.TestCase):
216211
path_prefix = '/test'
217212
def setUp(self):
218213
plugins = [
214+
FakePlugin(plugin_name='foo'),
219215
FakePlugin(
220-
None, plugin_name='foo', is_active_value=True, routes_mapping={}),
221-
FakePlugin(
222-
None,
223216
plugin_name='bar',
224217
is_active_value=False,
225-
routes_mapping={},
226218
element_name_value='tf-bar-dashboard',
227219
),
228220
FakePlugin(
229-
None,
230221
plugin_name='baz',
231-
is_active_value=True,
232222
routes_mapping={
233223
'/esmodule': lambda req: None,
234224
},
@@ -298,89 +288,73 @@ def testPluginsListing(self):
298288

299289
class ApplicationPluginNameTest(tb_test.TestCase):
300290

301-
def _test(self, name, should_be_okay):
302-
temp_dir = tempfile.mkdtemp(prefix=self.get_temp_dir())
303-
self.addCleanup(shutil.rmtree, temp_dir)
304-
multiplexer = event_multiplexer.EventMultiplexer(
305-
size_guidance=application.DEFAULT_SIZE_GUIDANCE,
306-
purge_orphaned_data=True)
307-
plugins = [
308-
FakePlugin(
309-
None, plugin_name='foo', is_active_value=True, routes_mapping={}),
310-
FakePlugin(
311-
None, plugin_name=name, is_active_value=True, routes_mapping={}),
312-
FakePlugin(
313-
None, plugin_name='bar', is_active_value=False, routes_mapping={}),
314-
]
315-
if should_be_okay:
316-
application.TensorBoardWSGIApp(
317-
temp_dir, plugins, multiplexer, reload_interval=0,
318-
path_prefix='')
319-
else:
320-
with six.assertRaisesRegex(self, ValueError, r'invalid name'):
321-
application.TensorBoardWSGIApp(
322-
temp_dir, plugins, multiplexer, reload_interval=0,
323-
path_prefix='')
291+
def testSimpleName(self):
292+
application.TensorBoardWSGI(
293+
plugins=[FakePlugin(plugin_name='scalars')])
294+
295+
def testComprehensiveName(self):
296+
application.TensorBoardWSGI(
297+
plugins=[FakePlugin(plugin_name='Scalar-Dashboard_3000.1')])
298+
299+
def testNameIsNone(self):
300+
with six.assertRaisesRegex(self, ValueError, r'no plugin_name'):
301+
application.TensorBoardWSGI(
302+
plugins=[FakePlugin(plugin_name=None)])
324303

325304
def testEmptyName(self):
326-
self._test('', False)
305+
with six.assertRaisesRegex(self, ValueError, r'invalid name'):
306+
application.TensorBoardWSGI(
307+
plugins=[FakePlugin(plugin_name='')])
327308

328309
def testNameWithSlashes(self):
329-
self._test('scalars/data', False)
310+
with six.assertRaisesRegex(self, ValueError, r'invalid name'):
311+
application.TensorBoardWSGI(
312+
plugins=[FakePlugin(plugin_name='scalars/data')])
330313

331314
def testNameWithSpaces(self):
332-
self._test('my favorite plugin', False)
315+
with six.assertRaisesRegex(self, ValueError, r'invalid name'):
316+
application.TensorBoardWSGI(
317+
plugins=[FakePlugin(plugin_name='my favorite plugin')])
333318

334-
def testSimpleName(self):
335-
self._test('scalars', True)
336-
337-
def testComprehensiveName(self):
338-
self._test('Scalar-Dashboard_3000.1', True)
319+
def testDuplicateName(self):
320+
with six.assertRaisesRegex(self, ValueError, r'Duplicate'):
321+
application.TensorBoardWSGI(
322+
plugins=[FakePlugin(plugin_name='scalars'),
323+
FakePlugin(plugin_name='scalars')])
339324

340325

341326
class ApplicationPluginRouteTest(tb_test.TestCase):
342327

343-
def _test(self, route, should_be_okay):
344-
temp_dir = tempfile.mkdtemp(prefix=self.get_temp_dir())
345-
self.addCleanup(shutil.rmtree, temp_dir)
346-
multiplexer = event_multiplexer.EventMultiplexer(
347-
size_guidance=application.DEFAULT_SIZE_GUIDANCE,
348-
purge_orphaned_data=True)
349-
plugins = [
350-
FakePlugin(
351-
None,
352-
plugin_name='foo',
353-
is_active_value=True,
354-
routes_mapping={route: lambda environ, start_response: None}),
355-
]
356-
if should_be_okay:
357-
application.TensorBoardWSGIApp(
358-
temp_dir, plugins, multiplexer, reload_interval=0, path_prefix='')
359-
else:
360-
with six.assertRaisesRegex(self, ValueError, r'invalid route'):
361-
application.TensorBoardWSGIApp(
362-
temp_dir, plugins, multiplexer, reload_interval=0, path_prefix='')
328+
def _make_plugin(self, route):
329+
return FakePlugin(
330+
plugin_name='foo',
331+
routes_mapping={route: lambda environ, start_response: None})
363332

364333
def testNormalRoute(self):
365-
self._test('/runs', True)
334+
application.TensorBoardWSGI([self._make_plugin('/runs')])
366335

367336
def testWildcardRoute(self):
368-
self._test('/foo/*', True)
337+
application.TensorBoardWSGI([self._make_plugin('/foo/*')])
369338

370339
def testNonPathComponentWildcardRoute(self):
371-
self._test('/foo*', False)
340+
with six.assertRaisesRegex(self, ValueError, r'invalid route'):
341+
application.TensorBoardWSGI([self._make_plugin('/foo*')])
372342

373343
def testMultiWildcardRoute(self):
374-
self._test('/foo/*/bar/*', False)
344+
with six.assertRaisesRegex(self, ValueError, r'invalid route'):
345+
application.TensorBoardWSGI([self._make_plugin('/foo/*/bar/*')])
375346

376347
def testInternalWildcardRoute(self):
377-
self._test('/foo/*/bar', False)
348+
with six.assertRaisesRegex(self, ValueError, r'invalid route'):
349+
application.TensorBoardWSGI([self._make_plugin('/foo/*/bar')])
378350

379351
def testEmptyRoute(self):
380-
self._test('', False)
352+
with six.assertRaisesRegex(self, ValueError, r'invalid route'):
353+
application.TensorBoardWSGI([self._make_plugin('')])
381354

382355
def testSlashlessRoute(self):
383-
self._test('runaway', False)
356+
with six.assertRaisesRegex(self, ValueError, r'invalid route'):
357+
application.TensorBoardWSGI([self._make_plugin('runaway')])
384358

385359

386360
class GetEventFileActiveFilterTest(tb_test.TestCase):
@@ -426,9 +400,9 @@ def assertPlatformSpecificLogdirParsing(self, pathObj, logdir, expected):
426400
pathObj: a custom replacement object for `os.path`, typically
427401
`posixpath` or `ntpath`
428402
logdir: the string to be parsed by
429-
:func:`~application.TensorBoardWSGIApp.parse_event_files_spec`
403+
:func:`~application.parse_event_files_spec`
430404
expected: the expected dictionary as returned by
431-
:func:`~application.TensorBoardWSGIApp.parse_event_files_spec`
405+
:func:`~application.parse_event_files_spec`
432406
433407
"""
434408

@@ -662,32 +636,6 @@ def testEmptyWildcardRouteWithSlash(self):
662636
self._test_route('/data/plugin/bar/wildcard/', 404)
663637

664638

665-
class ApplicationConstructionTest(tb_test.TestCase):
666-
667-
def testExceptions(self):
668-
logdir = '/fake/foo'
669-
multiplexer = event_multiplexer.EventMultiplexer()
670-
671-
# Fails if there is an unnamed plugin
672-
with self.assertRaises(ValueError):
673-
# This plugin lacks a name.
674-
plugins = [
675-
FakePlugin(
676-
None, plugin_name=None, is_active_value=True, routes_mapping={}),
677-
]
678-
application.TensorBoardWSGIApp(logdir, plugins, multiplexer, 0, '')
679-
680-
# Fails if there are two plugins with same name
681-
with self.assertRaises(ValueError):
682-
plugins = [
683-
FakePlugin(
684-
None, plugin_name='foo', is_active_value=True, routes_mapping={}),
685-
FakePlugin(
686-
None, plugin_name='foo', is_active_value=True, routes_mapping={}),
687-
]
688-
application.TensorBoardWSGIApp(logdir, plugins, multiplexer, 0, '')
689-
690-
691639
class DbTest(tb_test.TestCase):
692640

693641
def testSqliteDb(self):

tensorboard/plugins/audio/audio_plugin_test.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,12 @@ def setUp(self):
9292
"foo": foo_directory,
9393
"bar": bar_directory,
9494
})
95+
multiplexer.Reload()
9596
context = base_plugin.TBContext(
9697
logdir=self.log_dir, multiplexer=multiplexer)
9798
self.plugin = audio_plugin.AudioPlugin(context)
98-
# Setting a reload interval of -1 disables reloading. We disable reloading
99-
# because we seek to block tests from running til after one reload finishes.
100-
# This setUp method thus manually reloads the multiplexer. TensorBoard would
101-
# otherwise reload in a non-blocking thread.
102-
wsgi_app = application.TensorBoardWSGIApp(
103-
self.log_dir, [self.plugin], multiplexer, reload_interval=-1,
104-
path_prefix='')
99+
wsgi_app = application.TensorBoardWSGI([self.plugin])
105100
self.server = werkzeug_test.Client(wsgi_app, wrappers.BaseResponse)
106-
multiplexer.Reload()
107101

108102
def tearDown(self):
109103
shutil.rmtree(self.log_dir, ignore_errors=True)

tensorboard/plugins/core/core_plugin_test.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,12 +302,7 @@ def _start_logdir_based_server(self, temp_dir):
302302
multiplexer=self.multiplexer,
303303
window_title='title foo')
304304
self.logdir_based_plugin = core_plugin.CorePlugin(context)
305-
app = application.TensorBoardWSGIApp(
306-
self.logdir,
307-
[self.logdir_based_plugin],
308-
self.multiplexer,
309-
0,
310-
path_prefix='')
305+
app = application.TensorBoardWSGI([self.logdir_based_plugin])
311306
self.logdir_based_server = werkzeug_test.Client(app, wrappers.BaseResponse)
312307

313308
def _start_db_based_server(self):

tensorboard/plugins/debugger/debugger_plugin_testlib.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,11 @@ def setUp(self):
120120
writer.Close()
121121

122122
# Start a server that will receive requests and respond with health pills.
123-
self.multiplexer = event_multiplexer.EventMultiplexer({
123+
multiplexer = event_multiplexer.EventMultiplexer({
124124
'.': self.log_dir,
125125
'run_foo': run_foo_directory,
126126
})
127+
multiplexer.Reload()
127128
self.debugger_data_server_grpc_port = portpicker.pick_unused_port()
128129

129130
# Fake threading behavior so that threads are synchronous.
@@ -141,12 +142,10 @@ def setUp(self):
141142
self.mock_debugger_data_server_class).start()
142143

143144
self.context = base_plugin.TBContext(
144-
logdir=self.log_dir, multiplexer=self.multiplexer)
145+
logdir=self.log_dir, multiplexer=multiplexer)
145146
self.plugin = debugger_plugin.DebuggerPlugin(self.context)
146147
self.plugin.listen(self.debugger_data_server_grpc_port)
147-
wsgi_app = application.TensorBoardWSGIApp(
148-
self.log_dir, [self.plugin], self.multiplexer, reload_interval=0,
149-
path_prefix='')
148+
wsgi_app = application.TensorBoardWSGI([self.plugin])
150149
self.server = werkzeug_test.Client(wsgi_app, wrappers.BaseResponse)
151150

152151
# The debugger data server should be started at the correct port.

tensorboard/plugins/debugger/interactive_debugger_plugin_test.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,16 @@ def setUp(self):
5555
super(InteractiveDebuggerPluginTest, self).setUp()
5656

5757
self._dummy_logdir = tempfile.mkdtemp()
58-
self._dummy_multiplexer = event_multiplexer.EventMultiplexer({})
58+
dummy_multiplexer = event_multiplexer.EventMultiplexer({})
5959
self._debugger_port = portpicker.pick_unused_port()
6060
self._debugger_url = 'grpc://localhost:%d' % self._debugger_port
6161
context = base_plugin.TBContext(logdir=self._dummy_logdir,
62-
multiplexer=self._dummy_multiplexer)
62+
multiplexer=dummy_multiplexer)
6363
self._debugger_plugin = (
6464
interactive_debugger_plugin.InteractiveDebuggerPlugin(context))
6565
self._debugger_plugin.listen(self._debugger_port)
6666

67-
wsgi_app = application.TensorBoardWSGIApp(
68-
self._dummy_logdir,
69-
[self._debugger_plugin],
70-
self._dummy_multiplexer,
71-
reload_interval=0,
72-
path_prefix='')
67+
wsgi_app = application.TensorBoardWSGI([self._debugger_plugin])
7368
self._server = werkzeug_test.Client(wsgi_app, wrappers.BaseResponse)
7469

7570
def tearDown(self):

tensorboard/plugins/image/images_plugin_test.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,12 @@ def setUp(self):
8686
"foo": foo_directory,
8787
"bar": bar_directory,
8888
})
89+
multiplexer.Reload()
8990
context = base_plugin.TBContext(
9091
logdir=self.log_dir, multiplexer=multiplexer)
9192
plugin = images_plugin.ImagesPlugin(context)
92-
# Setting a reload interval of -1 disables reloading. We disable reloading
93-
# because we seek to block tests from running til after one reload finishes.
94-
# This setUp method thus manually reloads the multiplexer. TensorBoard would
95-
# otherwise reload in a non-blocking thread.
96-
wsgi_app = application.TensorBoardWSGIApp(
97-
self.log_dir, [plugin], multiplexer, reload_interval=-1, path_prefix='')
93+
wsgi_app = application.TensorBoardWSGI([plugin])
9894
self.server = werkzeug_test.Client(wsgi_app, wrappers.BaseResponse)
99-
multiplexer.Reload()
10095
self.routes = plugin.get_plugin_apps()
10196

10297
def tearDown(self):

0 commit comments

Comments
 (0)