Skip to content

Commit 4db1c24

Browse files
committed
Add the tensorboard.summary public entry point
Summary: Resolves #561. Test Plan: Ran the `build_pip_package.sh` script to build the Python 2 package, then created a new Python 2 virtualenv in which I installed only the latest nightly TensorFlow release (2017-09-22) and the generated wheel. Then wrote ```py import tensorflow as tf from tensorboard import summary result = tf.Session().run(summary.scalar('shelby', tf.constant(3))) s = tf.Summary() s.ParseFromString(result) print(s) ``` and verified that the result was a proper scalar summary. I then repeated the process using `pip install tensorflow==1.3.0` instead of the nightly version to ensure that the module works for normal TensorFlow users. (Note: the audio summary will not work because TensorFlow 1.3 lacks 22730fd4c633a74e59c03ff76dc92e6ae2d5d020. This is fine; users can continue to use the old API for that one.) wchargin-branch: summary-module
1 parent d01f82c commit 4db1c24

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

tensorboard/BUILD

+26
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,32 @@ py_test(
261261
],
262262
)
263263

264+
py_library(
265+
name = "summary",
266+
srcs = ["summary.py"],
267+
srcs_version = "PY2AND3",
268+
visibility = ["//visibility:public"],
269+
deps = [
270+
"//tensorboard/plugins/audio:summary",
271+
"//tensorboard/plugins/histogram:summary",
272+
"//tensorboard/plugins/image:summary",
273+
"//tensorboard/plugins/pr_curve:summary",
274+
"//tensorboard/plugins/scalar:summary",
275+
"//tensorboard/plugins/text:summary",
276+
],
277+
)
278+
279+
py_test(
280+
name = "summary_test",
281+
size = "small",
282+
srcs = ["summary_test.py"],
283+
srcs_version = "PY2AND3",
284+
deps = [
285+
":summary",
286+
"//tensorboard:expect_tensorflow_installed",
287+
],
288+
)
289+
264290
py_library(
265291
name = "test_util",
266292
testonly = 1,

tensorboard/pip_package/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ sh_binary(
2626
"setup.py",
2727
"//tensorboard",
2828
"//tensorboard:version",
29+
"//tensorboard:summary",
2930
],
3031
)

tensorboard/summary.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ==============================================================================
15+
"""Central API entry point for summary operations.
16+
17+
This module simply offers a shorter way to access the members of modules
18+
like `tensorboard.plugins.scalar.summary`.
19+
"""
20+
from __future__ import absolute_import
21+
from __future__ import division
22+
from __future__ import print_function
23+
24+
from tensorboard.plugins.audio import summary as _audio_summary
25+
from tensorboard.plugins.histogram import summary as _histogram_summary
26+
from tensorboard.plugins.image import summary as _image_summary
27+
from tensorboard.plugins.pr_curve import summary as _pr_curve_summary
28+
from tensorboard.plugins.scalar import summary as _scalar_summary
29+
from tensorboard.plugins.text import summary as _text_summary
30+
31+
32+
audio = _audio_summary.op
33+
audio_pb = _audio_summary.pb
34+
35+
histogram = _histogram_summary.op
36+
histogram_pb = _histogram_summary.pb
37+
38+
image = _image_summary.op
39+
image_pb = _image_summary.pb
40+
41+
pr_curve = _pr_curve_summary.op
42+
pr_curve_raw_data = _pr_curve_summary.raw_data_op
43+
44+
scalar = _scalar_summary.op
45+
scalar_pb = _scalar_summary.pb
46+
47+
text = _text_summary.op
48+
text_pb = _text_summary.pb

tensorboard/summary_test.py

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ==============================================================================
15+
"""API tests for the `tensorboard.summary` module.
16+
17+
These tests are especially important because this module is the standard
18+
public entry point for end users, so we should be as careful as possible
19+
to ensure that we export the right things.
20+
"""
21+
from __future__ import absolute_import
22+
from __future__ import division
23+
from __future__ import print_function
24+
25+
import collections
26+
27+
import tensorflow as tf
28+
from tensorboard import summary
29+
30+
31+
STANDARD_PLUGINS = frozenset([
32+
'audio',
33+
'histogram',
34+
'image',
35+
'pr_curve',
36+
'scalar',
37+
'text',
38+
])
39+
40+
# The subset of `STANDARD_PLUGINS` for which we do not currently have
41+
# functions to generate a summary protobuf outside of a TensorFlow
42+
# graph. This set should ideally be empty; any entries here should be
43+
# considered temporary.
44+
PLUGINS_WITHOUT_PB_FUNCTIONS = frozenset([
45+
'pr_curve', # TODO(@chihuahua, #445): Fix this.
46+
])
47+
48+
49+
class SummaryExportsTest(tf.test.TestCase):
50+
51+
def test_each_plugin_has_an_export(self):
52+
for plugin in STANDARD_PLUGINS:
53+
self.assertIsInstance(getattr(summary, plugin), collections.Callable)
54+
55+
def test_plugins_export_pb_functions(self):
56+
for plugin in STANDARD_PLUGINS:
57+
if plugin not in PLUGINS_WITHOUT_PB_FUNCTIONS:
58+
self.assertIsInstance(
59+
getattr(summary, '%s_pb' % plugin), collections.Callable)
60+
61+
def test_all_exports_correspond_to_plugins(self):
62+
exports = [name for name in dir(summary) if not name.startswith('_')]
63+
futures = frozenset(('absolute_import', 'division', 'print_function'))
64+
bad_exports = [
65+
name for name in exports
66+
if name not in futures and not any(
67+
name == plugin or name.startswith('%s_' % plugin)
68+
for plugin in STANDARD_PLUGINS)
69+
]
70+
if bad_exports:
71+
self.fail(
72+
'The following exports do not correspond to known standard '
73+
'plugins: %r. Please mark these as private by prepending an '
74+
'underscore to their names, or, if they correspond to a new '
75+
'plugin that you are certain should be part of the public API '
76+
'forever, add that plugin to the STANDARD_PLUGINS set in this '
77+
'module.' % bad_exports)
78+
79+
80+
if __name__ == '__main__':
81+
tf.test.main()

0 commit comments

Comments
 (0)