# Copyright 2017 The TensorFlow Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== """TensorBoard Plugin asset abstract class. TensorBoard plugins may need to provide arbitrary assets, such as configuration information for specific outputs, or vocabulary files, or sprite images, etc. This module contains methods that allow plugin assets to be specified at graph construction time. Plugin authors define a PluginAsset which is treated as a singleton on a per-graph basis. The PluginAsset has an assets method which returns a dictionary of asset contents. The tf.summary.FileWriter (or any other Summary writer) will serialize these assets in such a way that TensorBoard can retrieve them. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import abc from tensorflow.python.framework import ops _PLUGIN_ASSET_PREFIX = "__tensorboard_plugin_asset__" def get_plugin_asset(plugin_asset_cls, graph=None): """Acquire singleton PluginAsset instance from a graph. PluginAssets are always singletons, and are stored in tf Graph collections. This way, they can be defined anywhere the graph is being constructed, and if the same plugin is configured at many different points, the user can always modify the same instance. Args: plugin_asset_cls: The PluginAsset class graph: (optional) The graph to retrieve the instance from. If not specified, the default graph is used. Returns: An instance of the plugin_asset_class Raises: ValueError: If we have a plugin name collision, or if we unexpectedly find the wrong number of items in a collection. """ if graph is None: graph = ops.get_default_graph() if not plugin_asset_cls.plugin_name: raise ValueError("Class %s has no plugin_name" % plugin_asset_cls.__name__) name = _PLUGIN_ASSET_PREFIX + plugin_asset_cls.plugin_name container = graph.get_collection(name) if container: if len(container) is not 1: raise ValueError("Collection for %s had %d items, expected 1" % (name, len(container))) instance = container[0] if not isinstance(instance, plugin_asset_cls): raise ValueError("Plugin name collision between classes %s and %s" % (plugin_asset_cls.__name__, instance.__class__.__name__)) else: instance = plugin_asset_cls() graph.add_to_collection(name, instance) graph.add_to_collection(_PLUGIN_ASSET_PREFIX, plugin_asset_cls.plugin_name) return instance def get_all_plugin_assets(graph=None): """Retrieve all PluginAssets stored in the graph collection. Args: graph: Optionally, the graph to get assets from. If unspecified, the default graph is used. Returns: A list with all PluginAsset instances in the graph. Raises: ValueError: if we unexpectedly find a collection with the wrong number of PluginAssets. """ if graph is None: graph = ops.get_default_graph() out = [] for name in graph.get_collection(_PLUGIN_ASSET_PREFIX): collection = graph.get_collection(_PLUGIN_ASSET_PREFIX + name) if len(collection) is not 1: raise ValueError("Collection for %s had %d items, expected 1" % (name, len(collection))) out.append(collection[0]) return out class PluginAsset(object): """This abstract base class allows TensorBoard to serialize assets to disk. Plugin authors are expected to extend the PluginAsset class, so that it: - has a unique plugin_name - provides an assets method that returns an {asset_name: asset_contents} dictionary. For now, asset_contents are strings, although we may add StringIO support later. LifeCycle of a PluginAsset instance: - It is constructed when get_plugin_asset is called on the class for the first time. - It is configured by code that follows the calls to get_plugin_asset - When the containing graph is serialized by the tf.summary.FileWriter, the writer calls assets and the PluginAsset instance provides its contents to be written to disk. """ __metaclass__ = abc.ABCMeta plugin_name = None @abc.abstractmethod def assets(self): """Provide all of the assets contained by the PluginAsset instance. The assets method should return a dictionary structured as {asset_name: asset_contents}. asset_contents is a string. This method will be called by the tf.summary.FileWriter when it is time to write the assets out to disk. """ raise NotImplementedError()