tensorbored 2.21.0rc1769983804__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tensorbored/__init__.py +112 -0
- tensorbored/_vendor/__init__.py +0 -0
- tensorbored/_vendor/bleach/__init__.py +125 -0
- tensorbored/_vendor/bleach/_vendor/__init__.py +0 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/__init__.py +35 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_ihatexml.py +289 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_inputstream.py +918 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_tokenizer.py +1735 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_trie/__init__.py +5 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_trie/_base.py +40 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_trie/py.py +67 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/_utils.py +159 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/constants.py +2946 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/__init__.py +0 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/alphabeticalattributes.py +29 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/base.py +12 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/inject_meta_charset.py +73 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/lint.py +93 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/optionaltags.py +207 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/sanitizer.py +916 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/filters/whitespace.py +38 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/html5parser.py +2795 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/serializer.py +409 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/__init__.py +30 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/genshi.py +54 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treeadapters/sax.py +50 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/__init__.py +88 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/base.py +417 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/dom.py +239 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/etree.py +343 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treebuilders/etree_lxml.py +392 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/__init__.py +154 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/base.py +252 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/dom.py +43 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/etree.py +131 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/etree_lxml.py +215 -0
- tensorbored/_vendor/bleach/_vendor/html5lib/treewalkers/genshi.py +69 -0
- tensorbored/_vendor/bleach/_vendor/parse.py +1078 -0
- tensorbored/_vendor/bleach/callbacks.py +32 -0
- tensorbored/_vendor/bleach/html5lib_shim.py +757 -0
- tensorbored/_vendor/bleach/linkifier.py +633 -0
- tensorbored/_vendor/bleach/parse_shim.py +1 -0
- tensorbored/_vendor/bleach/sanitizer.py +638 -0
- tensorbored/_vendor/bleach/six_shim.py +19 -0
- tensorbored/_vendor/webencodings/__init__.py +342 -0
- tensorbored/_vendor/webencodings/labels.py +231 -0
- tensorbored/_vendor/webencodings/mklabels.py +59 -0
- tensorbored/_vendor/webencodings/x_user_defined.py +325 -0
- tensorbored/assets.py +36 -0
- tensorbored/auth.py +102 -0
- tensorbored/backend/__init__.py +0 -0
- tensorbored/backend/application.py +604 -0
- tensorbored/backend/auth_context_middleware.py +38 -0
- tensorbored/backend/client_feature_flags.py +113 -0
- tensorbored/backend/empty_path_redirect.py +46 -0
- tensorbored/backend/event_processing/__init__.py +0 -0
- tensorbored/backend/event_processing/data_ingester.py +276 -0
- tensorbored/backend/event_processing/data_provider.py +535 -0
- tensorbored/backend/event_processing/directory_loader.py +142 -0
- tensorbored/backend/event_processing/directory_watcher.py +272 -0
- tensorbored/backend/event_processing/event_accumulator.py +950 -0
- tensorbored/backend/event_processing/event_file_inspector.py +463 -0
- tensorbored/backend/event_processing/event_file_loader.py +292 -0
- tensorbored/backend/event_processing/event_multiplexer.py +521 -0
- tensorbored/backend/event_processing/event_util.py +68 -0
- tensorbored/backend/event_processing/io_wrapper.py +223 -0
- tensorbored/backend/event_processing/plugin_asset_util.py +104 -0
- tensorbored/backend/event_processing/plugin_event_accumulator.py +721 -0
- tensorbored/backend/event_processing/plugin_event_multiplexer.py +522 -0
- tensorbored/backend/event_processing/reservoir.py +266 -0
- tensorbored/backend/event_processing/tag_types.py +29 -0
- tensorbored/backend/experiment_id.py +71 -0
- tensorbored/backend/experimental_plugin.py +51 -0
- tensorbored/backend/http_util.py +263 -0
- tensorbored/backend/json_util.py +70 -0
- tensorbored/backend/path_prefix.py +67 -0
- tensorbored/backend/process_graph.py +74 -0
- tensorbored/backend/security_validator.py +202 -0
- tensorbored/compat/__init__.py +69 -0
- tensorbored/compat/proto/__init__.py +0 -0
- tensorbored/compat/proto/allocation_description_pb2.py +35 -0
- tensorbored/compat/proto/api_def_pb2.py +82 -0
- tensorbored/compat/proto/attr_value_pb2.py +80 -0
- tensorbored/compat/proto/cluster_pb2.py +58 -0
- tensorbored/compat/proto/config_pb2.py +271 -0
- tensorbored/compat/proto/coordination_config_pb2.py +45 -0
- tensorbored/compat/proto/cost_graph_pb2.py +87 -0
- tensorbored/compat/proto/cpp_shape_inference_pb2.py +70 -0
- tensorbored/compat/proto/debug_pb2.py +65 -0
- tensorbored/compat/proto/event_pb2.py +149 -0
- tensorbored/compat/proto/full_type_pb2.py +74 -0
- tensorbored/compat/proto/function_pb2.py +157 -0
- tensorbored/compat/proto/graph_debug_info_pb2.py +111 -0
- tensorbored/compat/proto/graph_pb2.py +41 -0
- tensorbored/compat/proto/histogram_pb2.py +39 -0
- tensorbored/compat/proto/meta_graph_pb2.py +254 -0
- tensorbored/compat/proto/node_def_pb2.py +61 -0
- tensorbored/compat/proto/op_def_pb2.py +81 -0
- tensorbored/compat/proto/resource_handle_pb2.py +48 -0
- tensorbored/compat/proto/rewriter_config_pb2.py +93 -0
- tensorbored/compat/proto/rpc_options_pb2.py +35 -0
- tensorbored/compat/proto/saved_object_graph_pb2.py +193 -0
- tensorbored/compat/proto/saver_pb2.py +38 -0
- tensorbored/compat/proto/step_stats_pb2.py +116 -0
- tensorbored/compat/proto/struct_pb2.py +144 -0
- tensorbored/compat/proto/summary_pb2.py +111 -0
- tensorbored/compat/proto/tensor_description_pb2.py +38 -0
- tensorbored/compat/proto/tensor_pb2.py +68 -0
- tensorbored/compat/proto/tensor_shape_pb2.py +46 -0
- tensorbored/compat/proto/tfprof_log_pb2.py +307 -0
- tensorbored/compat/proto/trackable_object_graph_pb2.py +90 -0
- tensorbored/compat/proto/types_pb2.py +105 -0
- tensorbored/compat/proto/variable_pb2.py +62 -0
- tensorbored/compat/proto/verifier_config_pb2.py +38 -0
- tensorbored/compat/proto/versions_pb2.py +35 -0
- tensorbored/compat/tensorflow_stub/__init__.py +38 -0
- tensorbored/compat/tensorflow_stub/app.py +124 -0
- tensorbored/compat/tensorflow_stub/compat/__init__.py +131 -0
- tensorbored/compat/tensorflow_stub/compat/v1/__init__.py +20 -0
- tensorbored/compat/tensorflow_stub/dtypes.py +692 -0
- tensorbored/compat/tensorflow_stub/error_codes.py +169 -0
- tensorbored/compat/tensorflow_stub/errors.py +507 -0
- tensorbored/compat/tensorflow_stub/flags.py +124 -0
- tensorbored/compat/tensorflow_stub/io/__init__.py +17 -0
- tensorbored/compat/tensorflow_stub/io/gfile.py +1011 -0
- tensorbored/compat/tensorflow_stub/pywrap_tensorflow.py +285 -0
- tensorbored/compat/tensorflow_stub/tensor_shape.py +1035 -0
- tensorbored/context.py +129 -0
- tensorbored/data/__init__.py +0 -0
- tensorbored/data/grpc_provider.py +365 -0
- tensorbored/data/ingester.py +46 -0
- tensorbored/data/proto/__init__.py +0 -0
- tensorbored/data/proto/data_provider_pb2.py +517 -0
- tensorbored/data/proto/data_provider_pb2_grpc.py +374 -0
- tensorbored/data/provider.py +1365 -0
- tensorbored/data/server_ingester.py +301 -0
- tensorbored/data_compat.py +159 -0
- tensorbored/dataclass_compat.py +224 -0
- tensorbored/default.py +124 -0
- tensorbored/errors.py +130 -0
- tensorbored/lazy.py +99 -0
- tensorbored/main.py +48 -0
- tensorbored/main_lib.py +62 -0
- tensorbored/manager.py +487 -0
- tensorbored/notebook.py +441 -0
- tensorbored/plugin_util.py +266 -0
- tensorbored/plugins/__init__.py +0 -0
- tensorbored/plugins/audio/__init__.py +0 -0
- tensorbored/plugins/audio/audio_plugin.py +229 -0
- tensorbored/plugins/audio/metadata.py +69 -0
- tensorbored/plugins/audio/plugin_data_pb2.py +37 -0
- tensorbored/plugins/audio/summary.py +230 -0
- tensorbored/plugins/audio/summary_v2.py +124 -0
- tensorbored/plugins/base_plugin.py +367 -0
- tensorbored/plugins/core/__init__.py +0 -0
- tensorbored/plugins/core/core_plugin.py +981 -0
- tensorbored/plugins/custom_scalar/__init__.py +0 -0
- tensorbored/plugins/custom_scalar/custom_scalars_plugin.py +320 -0
- tensorbored/plugins/custom_scalar/layout_pb2.py +85 -0
- tensorbored/plugins/custom_scalar/metadata.py +35 -0
- tensorbored/plugins/custom_scalar/summary.py +79 -0
- tensorbored/plugins/debugger_v2/__init__.py +0 -0
- tensorbored/plugins/debugger_v2/debug_data_multiplexer.py +631 -0
- tensorbored/plugins/debugger_v2/debug_data_provider.py +634 -0
- tensorbored/plugins/debugger_v2/debugger_v2_plugin.py +504 -0
- tensorbored/plugins/distribution/__init__.py +0 -0
- tensorbored/plugins/distribution/compressor.py +158 -0
- tensorbored/plugins/distribution/distributions_plugin.py +116 -0
- tensorbored/plugins/distribution/metadata.py +19 -0
- tensorbored/plugins/graph/__init__.py +0 -0
- tensorbored/plugins/graph/graph_util.py +129 -0
- tensorbored/plugins/graph/graphs_plugin.py +336 -0
- tensorbored/plugins/graph/keras_util.py +328 -0
- tensorbored/plugins/graph/metadata.py +42 -0
- tensorbored/plugins/histogram/__init__.py +0 -0
- tensorbored/plugins/histogram/histograms_plugin.py +144 -0
- tensorbored/plugins/histogram/metadata.py +63 -0
- tensorbored/plugins/histogram/plugin_data_pb2.py +34 -0
- tensorbored/plugins/histogram/summary.py +234 -0
- tensorbored/plugins/histogram/summary_v2.py +292 -0
- tensorbored/plugins/hparams/__init__.py +14 -0
- tensorbored/plugins/hparams/_keras.py +93 -0
- tensorbored/plugins/hparams/api.py +130 -0
- tensorbored/plugins/hparams/api_pb2.py +208 -0
- tensorbored/plugins/hparams/backend_context.py +606 -0
- tensorbored/plugins/hparams/download_data.py +158 -0
- tensorbored/plugins/hparams/error.py +26 -0
- tensorbored/plugins/hparams/get_experiment.py +71 -0
- tensorbored/plugins/hparams/hparams_plugin.py +206 -0
- tensorbored/plugins/hparams/hparams_util_pb2.py +69 -0
- tensorbored/plugins/hparams/json_format_compat.py +38 -0
- tensorbored/plugins/hparams/list_metric_evals.py +57 -0
- tensorbored/plugins/hparams/list_session_groups.py +1040 -0
- tensorbored/plugins/hparams/metadata.py +125 -0
- tensorbored/plugins/hparams/metrics.py +41 -0
- tensorbored/plugins/hparams/plugin_data_pb2.py +69 -0
- tensorbored/plugins/hparams/summary.py +205 -0
- tensorbored/plugins/hparams/summary_v2.py +597 -0
- tensorbored/plugins/image/__init__.py +0 -0
- tensorbored/plugins/image/images_plugin.py +232 -0
- tensorbored/plugins/image/metadata.py +65 -0
- tensorbored/plugins/image/plugin_data_pb2.py +34 -0
- tensorbored/plugins/image/summary.py +159 -0
- tensorbored/plugins/image/summary_v2.py +130 -0
- tensorbored/plugins/mesh/__init__.py +14 -0
- tensorbored/plugins/mesh/mesh_plugin.py +292 -0
- tensorbored/plugins/mesh/metadata.py +152 -0
- tensorbored/plugins/mesh/plugin_data_pb2.py +37 -0
- tensorbored/plugins/mesh/summary.py +251 -0
- tensorbored/plugins/mesh/summary_v2.py +214 -0
- tensorbored/plugins/metrics/__init__.py +0 -0
- tensorbored/plugins/metrics/metadata.py +17 -0
- tensorbored/plugins/metrics/metrics_plugin.py +623 -0
- tensorbored/plugins/pr_curve/__init__.py +0 -0
- tensorbored/plugins/pr_curve/metadata.py +75 -0
- tensorbored/plugins/pr_curve/plugin_data_pb2.py +34 -0
- tensorbored/plugins/pr_curve/pr_curves_plugin.py +241 -0
- tensorbored/plugins/pr_curve/summary.py +574 -0
- tensorbored/plugins/profile_redirect/__init__.py +0 -0
- tensorbored/plugins/profile_redirect/profile_redirect_plugin.py +49 -0
- tensorbored/plugins/projector/__init__.py +67 -0
- tensorbored/plugins/projector/metadata.py +26 -0
- tensorbored/plugins/projector/projector_config_pb2.py +54 -0
- tensorbored/plugins/projector/projector_plugin.py +795 -0
- tensorbored/plugins/projector/tf_projector_plugin/index.js +32 -0
- tensorbored/plugins/projector/tf_projector_plugin/projector_binary.html +524 -0
- tensorbored/plugins/projector/tf_projector_plugin/projector_binary.js +15536 -0
- tensorbored/plugins/scalar/__init__.py +0 -0
- tensorbored/plugins/scalar/metadata.py +60 -0
- tensorbored/plugins/scalar/plugin_data_pb2.py +34 -0
- tensorbored/plugins/scalar/scalars_plugin.py +181 -0
- tensorbored/plugins/scalar/summary.py +109 -0
- tensorbored/plugins/scalar/summary_v2.py +124 -0
- tensorbored/plugins/text/__init__.py +0 -0
- tensorbored/plugins/text/metadata.py +62 -0
- tensorbored/plugins/text/plugin_data_pb2.py +34 -0
- tensorbored/plugins/text/summary.py +114 -0
- tensorbored/plugins/text/summary_v2.py +124 -0
- tensorbored/plugins/text/text_plugin.py +288 -0
- tensorbored/plugins/wit_redirect/__init__.py +0 -0
- tensorbored/plugins/wit_redirect/wit_redirect_plugin.py +49 -0
- tensorbored/program.py +910 -0
- tensorbored/summary/__init__.py +35 -0
- tensorbored/summary/_output.py +124 -0
- tensorbored/summary/_tf/__init__.py +14 -0
- tensorbored/summary/_tf/summary/__init__.py +178 -0
- tensorbored/summary/_writer.py +105 -0
- tensorbored/summary/v1.py +51 -0
- tensorbored/summary/v2.py +25 -0
- tensorbored/summary/writer/__init__.py +13 -0
- tensorbored/summary/writer/event_file_writer.py +291 -0
- tensorbored/summary/writer/record_writer.py +50 -0
- tensorbored/util/__init__.py +0 -0
- tensorbored/util/encoder.py +116 -0
- tensorbored/util/grpc_util.py +311 -0
- tensorbored/util/img_mime_type_detector.py +40 -0
- tensorbored/util/io_util.py +20 -0
- tensorbored/util/lazy_tensor_creator.py +110 -0
- tensorbored/util/op_evaluator.py +104 -0
- tensorbored/util/platform_util.py +20 -0
- tensorbored/util/tb_logging.py +24 -0
- tensorbored/util/tensor_util.py +617 -0
- tensorbored/util/timing.py +122 -0
- tensorbored/version.py +21 -0
- tensorbored/webfiles.zip +0 -0
- tensorbored-2.21.0rc1769983804.dist-info/METADATA +49 -0
- tensorbored-2.21.0rc1769983804.dist-info/RECORD +271 -0
- tensorbored-2.21.0rc1769983804.dist-info/WHEEL +5 -0
- tensorbored-2.21.0rc1769983804.dist-info/entry_points.txt +6 -0
- tensorbored-2.21.0rc1769983804.dist-info/licenses/LICENSE +739 -0
- tensorbored-2.21.0rc1769983804.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,114 @@
|
|
|
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
|
+
"""Text summaries and TensorFlow operations to create them."""
|
|
16
|
+
|
|
17
|
+
from tensorbored.plugins.text import metadata
|
|
18
|
+
from tensorbored.plugins.text import summary_v2
|
|
19
|
+
|
|
20
|
+
# Export V2 versions.
|
|
21
|
+
text = summary_v2.text
|
|
22
|
+
text_pb = summary_v2.text_pb
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def op(name, data, display_name=None, description=None, collections=None):
|
|
26
|
+
"""Create a legacy text summary op.
|
|
27
|
+
|
|
28
|
+
Text data summarized via this plugin will be visible in the Text Dashboard
|
|
29
|
+
in TensorBoard. The standard TensorBoard Text Dashboard will render markdown
|
|
30
|
+
in the strings, and will automatically organize 1D and 2D tensors into tables.
|
|
31
|
+
If a tensor with more than 2 dimensions is provided, a 2D subarray will be
|
|
32
|
+
displayed along with a warning message. (Note that this behavior is not
|
|
33
|
+
intrinsic to the text summary API, but rather to the default TensorBoard text
|
|
34
|
+
plugin.)
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
name: A name for the generated node. Will also serve as a series name in
|
|
38
|
+
TensorBoard.
|
|
39
|
+
data: A string-type Tensor to summarize. The text must be encoded in UTF-8.
|
|
40
|
+
display_name: Optional name for this summary in TensorBoard, as a
|
|
41
|
+
constant `str`. Defaults to `name`.
|
|
42
|
+
description: Optional long-form description for this summary, as a
|
|
43
|
+
constant `str`. Markdown is supported. Defaults to empty.
|
|
44
|
+
collections: Optional list of ops.GraphKeys. The collections to which to add
|
|
45
|
+
the summary. Defaults to [Graph Keys.SUMMARIES].
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A TensorSummary op that is configured so that TensorBoard will recognize
|
|
49
|
+
that it contains textual data. The TensorSummary is a scalar `Tensor` of
|
|
50
|
+
type `string` which contains `Summary` protobufs.
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
ValueError: If tensor has the wrong type.
|
|
54
|
+
"""
|
|
55
|
+
# TODO(nickfelt): remove on-demand imports once dep situation is fixed.
|
|
56
|
+
import tensorflow.compat.v1 as tf
|
|
57
|
+
|
|
58
|
+
if display_name is None:
|
|
59
|
+
display_name = name
|
|
60
|
+
summary_metadata = metadata.create_summary_metadata(
|
|
61
|
+
display_name=display_name, description=description
|
|
62
|
+
)
|
|
63
|
+
with tf.name_scope(name):
|
|
64
|
+
with tf.control_dependencies([tf.assert_type(data, tf.string)]):
|
|
65
|
+
return tf.summary.tensor_summary(
|
|
66
|
+
name="text_summary",
|
|
67
|
+
tensor=data,
|
|
68
|
+
collections=collections,
|
|
69
|
+
summary_metadata=summary_metadata,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def pb(name, data, display_name=None, description=None):
|
|
74
|
+
"""Create a legacy text summary protobuf.
|
|
75
|
+
|
|
76
|
+
Arguments:
|
|
77
|
+
name: A name for the generated node. Will also serve as a series name in
|
|
78
|
+
TensorBoard.
|
|
79
|
+
data: A Python bytestring (of type bytes), or Unicode string. Or a numpy
|
|
80
|
+
data array of those types.
|
|
81
|
+
display_name: Optional name for this summary in TensorBoard, as a
|
|
82
|
+
`str`. Defaults to `name`.
|
|
83
|
+
description: Optional long-form description for this summary, as a
|
|
84
|
+
`str`. Markdown is supported. Defaults to empty.
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
ValueError: If the type of the data is unsupported.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
A `tf.Summary` protobuf object.
|
|
91
|
+
"""
|
|
92
|
+
# TODO(nickfelt): remove on-demand imports once dep situation is fixed.
|
|
93
|
+
import tensorflow.compat.v1 as tf
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
tensor = tf.make_tensor_proto(data, dtype=tf.string)
|
|
97
|
+
except TypeError as e:
|
|
98
|
+
raise ValueError(e)
|
|
99
|
+
|
|
100
|
+
if display_name is None:
|
|
101
|
+
display_name = name
|
|
102
|
+
summary_metadata = metadata.create_summary_metadata(
|
|
103
|
+
display_name=display_name, description=description
|
|
104
|
+
)
|
|
105
|
+
tf_summary_metadata = tf.SummaryMetadata.FromString(
|
|
106
|
+
summary_metadata.SerializeToString()
|
|
107
|
+
)
|
|
108
|
+
summary = tf.Summary()
|
|
109
|
+
summary.value.add(
|
|
110
|
+
tag="%s/text_summary" % name,
|
|
111
|
+
metadata=tf_summary_metadata,
|
|
112
|
+
tensor=tensor,
|
|
113
|
+
)
|
|
114
|
+
return summary
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Copyright 2018 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
|
+
"""Text summaries and TensorFlow operations to create them, V2 versions."""
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
from tensorbored.compat import tf2 as tf
|
|
20
|
+
from tensorbored.compat.proto import summary_pb2
|
|
21
|
+
from tensorbored.plugins.text import metadata
|
|
22
|
+
from tensorbored.util import tensor_util
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def text(name, data, step=None, description=None):
|
|
26
|
+
r"""Write a text summary.
|
|
27
|
+
|
|
28
|
+
See also `tf.summary.scalar`, `tf.summary.SummaryWriter`, `tf.summary.image`.
|
|
29
|
+
|
|
30
|
+
Writes text Tensor values for later visualization and analysis in TensorBoard.
|
|
31
|
+
Writes go to the current default summary writer. Like `tf.summary.scalar`
|
|
32
|
+
points, text points are each associated with a `step` and a `name`.
|
|
33
|
+
All the points with the same `name` constitute a time series of text values.
|
|
34
|
+
|
|
35
|
+
For Example:
|
|
36
|
+
```python
|
|
37
|
+
test_summary_writer = tf.summary.create_file_writer('test/logdir')
|
|
38
|
+
with test_summary_writer.as_default():
|
|
39
|
+
tf.summary.text('first_text', 'hello world!', step=0)
|
|
40
|
+
tf.summary.text('first_text', 'nice to meet you!', step=1)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The text summary can also contain Markdown, and TensorBoard will render the text
|
|
44
|
+
as such.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
with test_summary_writer.as_default():
|
|
48
|
+
text_data = '''
|
|
49
|
+
| *hello* | *there* |
|
|
50
|
+
|---------|---------|
|
|
51
|
+
| this | is |
|
|
52
|
+
| a | table |
|
|
53
|
+
'''
|
|
54
|
+
text_data = '\n'.join(l.strip() for l in text_data.splitlines())
|
|
55
|
+
tf.summary.text('markdown_text', text_data, step=0)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Since text is Tensor valued, each text point may be a Tensor of string values.
|
|
59
|
+
rank-1 and rank-2 Tensors are rendered as tables in TensorBoard. For higher ranked
|
|
60
|
+
Tensors, you'll see just a 2D slice of the data. To avoid this, reshape the Tensor
|
|
61
|
+
to at most rank-2 prior to passing it to this function.
|
|
62
|
+
|
|
63
|
+
Demo notebook at
|
|
64
|
+
["Displaying text data in TensorBoard"](https://www.tensorflow.org/tensorboard/text_summaries).
|
|
65
|
+
|
|
66
|
+
Arguments:
|
|
67
|
+
name: A name for this summary. The summary tag used for TensorBoard will
|
|
68
|
+
be this name prefixed by any active name scopes.
|
|
69
|
+
data: A UTF-8 string Tensor value.
|
|
70
|
+
step: Explicit `int64`-castable monotonic step value for this summary. If
|
|
71
|
+
omitted, this defaults to `tf.summary.experimental.get_step()`, which must
|
|
72
|
+
not be None.
|
|
73
|
+
description: Optional long-form description for this summary, as a
|
|
74
|
+
constant `str`. Markdown is supported. Defaults to empty.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True on success, or false if no summary was emitted because no default
|
|
78
|
+
summary writer was available.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
ValueError: if a default writer exists, but no step was provided and
|
|
82
|
+
`tf.summary.experimental.get_step()` is None.
|
|
83
|
+
"""
|
|
84
|
+
summary_metadata = metadata.create_summary_metadata(
|
|
85
|
+
display_name=None, description=description
|
|
86
|
+
)
|
|
87
|
+
# TODO(https://github.com/tensorflow/tensorboard/issues/2109): remove fallback
|
|
88
|
+
summary_scope = (
|
|
89
|
+
getattr(tf.summary.experimental, "summary_scope", None)
|
|
90
|
+
or tf.summary.summary_scope
|
|
91
|
+
)
|
|
92
|
+
with summary_scope(name, "text_summary", values=[data, step]) as (tag, _):
|
|
93
|
+
tf.debugging.assert_type(data, tf.string)
|
|
94
|
+
return tf.summary.write(
|
|
95
|
+
tag=tag, tensor=data, step=step, metadata=summary_metadata
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def text_pb(tag, data, description=None):
|
|
100
|
+
"""Create a text tf.Summary protobuf.
|
|
101
|
+
|
|
102
|
+
Arguments:
|
|
103
|
+
tag: String tag for the summary.
|
|
104
|
+
data: A Python bytestring (of type bytes), a Unicode string, or a numpy data
|
|
105
|
+
array of those types.
|
|
106
|
+
description: Optional long-form description for this summary, as a `str`.
|
|
107
|
+
Markdown is supported. Defaults to empty.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
TypeError: If the type of the data is unsupported.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
A `tf.Summary` protobuf object.
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
tensor = tensor_util.make_tensor_proto(data, dtype=np.object_)
|
|
117
|
+
except TypeError as e:
|
|
118
|
+
raise TypeError("tensor must be of type string", e)
|
|
119
|
+
summary_metadata = metadata.create_summary_metadata(
|
|
120
|
+
display_name=None, description=description
|
|
121
|
+
)
|
|
122
|
+
summary = summary_pb2.Summary()
|
|
123
|
+
summary.value.add(tag=tag, metadata=summary_metadata, tensor=tensor)
|
|
124
|
+
return summary
|
|
@@ -0,0 +1,288 @@
|
|
|
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
|
+
"""The TensorBoard Text plugin."""
|
|
16
|
+
|
|
17
|
+
import textwrap
|
|
18
|
+
|
|
19
|
+
# pylint: disable=g-bad-import-order
|
|
20
|
+
# Necessary for an internal test with special behavior for numpy.
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
# pylint: enable=g-bad-import-order
|
|
24
|
+
|
|
25
|
+
from werkzeug import wrappers
|
|
26
|
+
|
|
27
|
+
from tensorbored import plugin_util
|
|
28
|
+
from tensorbored.backend import http_util
|
|
29
|
+
from tensorbored.data import provider
|
|
30
|
+
from tensorbored.plugins import base_plugin
|
|
31
|
+
from tensorbored.plugins.text import metadata
|
|
32
|
+
|
|
33
|
+
# HTTP routes
|
|
34
|
+
TAGS_ROUTE = "/tags"
|
|
35
|
+
TEXT_ROUTE = "/text"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
WARNING_TEMPLATE = textwrap.dedent(
|
|
39
|
+
"""\
|
|
40
|
+
**Warning:** This text summary contained data of dimensionality %d, but only \
|
|
41
|
+
2d tables are supported. Showing a 2d slice of the data instead."""
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
_DEFAULT_DOWNSAMPLING = 100 # text tensors per time series
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def make_table_row(contents, tag="td"):
|
|
48
|
+
"""Given an iterable of string contents, make a table row.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
contents: An iterable yielding strings.
|
|
52
|
+
tag: The tag to place contents in. Defaults to 'td', you might want 'th'.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
A string containing the content strings, organized into a table row.
|
|
56
|
+
|
|
57
|
+
Example: make_table_row(['one', 'two', 'three']) == '''
|
|
58
|
+
<tr>
|
|
59
|
+
<td>one</td>
|
|
60
|
+
<td>two</td>
|
|
61
|
+
<td>three</td>
|
|
62
|
+
</tr>'''
|
|
63
|
+
"""
|
|
64
|
+
columns = ("<%s>%s</%s>\n" % (tag, s, tag) for s in contents)
|
|
65
|
+
return "<tr>\n" + "".join(columns) + "</tr>\n"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def make_table(contents, headers=None):
|
|
69
|
+
"""Given a numpy ndarray of strings, concatenate them into a html table.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
contents: A np.ndarray of strings. May be 1d or 2d. In the 1d case, the
|
|
73
|
+
table is laid out vertically (i.e. row-major).
|
|
74
|
+
headers: A np.ndarray or list of string header names for the table.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A string containing all of the content strings, organized into a table.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: If contents is not a np.ndarray.
|
|
81
|
+
ValueError: If contents is not 1d or 2d.
|
|
82
|
+
ValueError: If contents is empty.
|
|
83
|
+
ValueError: If headers is present and not a list, tuple, or ndarray.
|
|
84
|
+
ValueError: If headers is not 1d.
|
|
85
|
+
ValueError: If number of elements in headers does not correspond to number
|
|
86
|
+
of columns in contents.
|
|
87
|
+
"""
|
|
88
|
+
if not isinstance(contents, np.ndarray):
|
|
89
|
+
raise ValueError("make_table contents must be a numpy ndarray")
|
|
90
|
+
|
|
91
|
+
if contents.ndim not in [1, 2]:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"make_table requires a 1d or 2d numpy array, was %dd"
|
|
94
|
+
% contents.ndim
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if headers:
|
|
98
|
+
if isinstance(headers, (list, tuple)):
|
|
99
|
+
headers = np.array(headers)
|
|
100
|
+
if not isinstance(headers, np.ndarray):
|
|
101
|
+
raise ValueError(
|
|
102
|
+
"Could not convert headers %s into np.ndarray" % headers
|
|
103
|
+
)
|
|
104
|
+
if headers.ndim != 1:
|
|
105
|
+
raise ValueError("Headers must be 1d, is %dd" % headers.ndim)
|
|
106
|
+
expected_n_columns = contents.shape[1] if contents.ndim == 2 else 1
|
|
107
|
+
if headers.shape[0] != expected_n_columns:
|
|
108
|
+
raise ValueError(
|
|
109
|
+
"Number of headers %d must match number of columns %d"
|
|
110
|
+
% (headers.shape[0], expected_n_columns)
|
|
111
|
+
)
|
|
112
|
+
header = "<thead>\n%s</thead>\n" % make_table_row(headers, tag="th")
|
|
113
|
+
else:
|
|
114
|
+
header = ""
|
|
115
|
+
|
|
116
|
+
n_rows = contents.shape[0]
|
|
117
|
+
if contents.ndim == 1:
|
|
118
|
+
# If it's a vector, we need to wrap each element in a new list, otherwise
|
|
119
|
+
# we would turn the string itself into a row (see test code)
|
|
120
|
+
rows = (make_table_row([contents[i]]) for i in range(n_rows))
|
|
121
|
+
else:
|
|
122
|
+
rows = (make_table_row(contents[i, :]) for i in range(n_rows))
|
|
123
|
+
|
|
124
|
+
return "<table>\n%s<tbody>\n%s</tbody>\n</table>" % (header, "".join(rows))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def reduce_to_2d(arr):
|
|
128
|
+
"""Given a np.npdarray with nDims > 2, reduce it to 2d.
|
|
129
|
+
|
|
130
|
+
It does this by selecting the zeroth coordinate for every dimension greater
|
|
131
|
+
than two.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
arr: a numpy ndarray of dimension at least 2.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
A two-dimensional subarray from the input array.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
ValueError: If the argument is not a numpy ndarray, or the dimensionality
|
|
141
|
+
is too low.
|
|
142
|
+
"""
|
|
143
|
+
if not isinstance(arr, np.ndarray):
|
|
144
|
+
raise ValueError("reduce_to_2d requires a numpy.ndarray")
|
|
145
|
+
|
|
146
|
+
ndims = len(arr.shape)
|
|
147
|
+
if ndims < 2:
|
|
148
|
+
raise ValueError("reduce_to_2d requires an array of dimensionality >=2")
|
|
149
|
+
# slice(None) is equivalent to `:`, so we take arr[0,0,...0,:,:]
|
|
150
|
+
slices = ([0] * (ndims - 2)) + [slice(None), slice(None)]
|
|
151
|
+
return arr[tuple(slices)]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def text_array_to_html(text_arr, enable_markdown):
|
|
155
|
+
"""Take a numpy.ndarray containing strings, and convert it into html.
|
|
156
|
+
|
|
157
|
+
If the ndarray contains a single scalar string, that string is converted to
|
|
158
|
+
html via our sanitized markdown parser. If it contains an array of strings,
|
|
159
|
+
the strings are individually converted to html and then composed into a table
|
|
160
|
+
using make_table. If the array contains dimensionality greater than 2,
|
|
161
|
+
all but two of the dimensions are removed, and a warning message is prefixed
|
|
162
|
+
to the table.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
text_arr: A numpy.ndarray containing strings.
|
|
166
|
+
enable_markdown: boolean, whether to enable Markdown
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
The array converted to html.
|
|
170
|
+
"""
|
|
171
|
+
if not text_arr.shape:
|
|
172
|
+
# It is a scalar. No need to put it in a table.
|
|
173
|
+
if enable_markdown:
|
|
174
|
+
return plugin_util.markdown_to_safe_html(text_arr.item())
|
|
175
|
+
else:
|
|
176
|
+
return plugin_util.safe_html(text_arr.item())
|
|
177
|
+
warning = ""
|
|
178
|
+
if len(text_arr.shape) > 2:
|
|
179
|
+
warning = plugin_util.markdown_to_safe_html(
|
|
180
|
+
WARNING_TEMPLATE % len(text_arr.shape)
|
|
181
|
+
)
|
|
182
|
+
text_arr = reduce_to_2d(text_arr)
|
|
183
|
+
if enable_markdown:
|
|
184
|
+
table = plugin_util.markdowns_to_safe_html(
|
|
185
|
+
text_arr.reshape(-1),
|
|
186
|
+
lambda xs: make_table(np.array(xs).reshape(text_arr.shape)),
|
|
187
|
+
)
|
|
188
|
+
else:
|
|
189
|
+
# Convert utf-8 bytes to str. The built-in np.char.decode doesn't work on
|
|
190
|
+
# object arrays, and converting to an numpy chararray is lossy.
|
|
191
|
+
decode = lambda bs: bs.decode("utf-8") if isinstance(bs, bytes) else bs
|
|
192
|
+
text_arr_str = np.array(
|
|
193
|
+
[decode(bs) for bs in text_arr.reshape(-1)]
|
|
194
|
+
).reshape(text_arr.shape)
|
|
195
|
+
table = plugin_util.safe_html(make_table(text_arr_str))
|
|
196
|
+
return warning + table
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def process_event(wall_time, step, string_ndarray, enable_markdown):
|
|
200
|
+
"""Convert a text event into a JSON-compatible response."""
|
|
201
|
+
html = text_array_to_html(string_ndarray, enable_markdown)
|
|
202
|
+
return {
|
|
203
|
+
"wall_time": wall_time,
|
|
204
|
+
"step": step,
|
|
205
|
+
"text": html,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class TextPlugin(base_plugin.TBPlugin):
|
|
210
|
+
"""Text Plugin for TensorBoard."""
|
|
211
|
+
|
|
212
|
+
plugin_name = metadata.PLUGIN_NAME
|
|
213
|
+
|
|
214
|
+
def __init__(self, context):
|
|
215
|
+
"""Instantiates TextPlugin via TensorBoard core.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
context: A base_plugin.TBContext instance.
|
|
219
|
+
"""
|
|
220
|
+
self._downsample_to = (context.sampling_hints or {}).get(
|
|
221
|
+
self.plugin_name, _DEFAULT_DOWNSAMPLING
|
|
222
|
+
)
|
|
223
|
+
self._data_provider = context.data_provider
|
|
224
|
+
self._version_checker = plugin_util._MetadataVersionChecker(
|
|
225
|
+
data_kind="text",
|
|
226
|
+
latest_known_version=0,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def is_active(self):
|
|
230
|
+
return False # `list_plugins` as called by TB core suffices
|
|
231
|
+
|
|
232
|
+
def frontend_metadata(self):
|
|
233
|
+
return base_plugin.FrontendMetadata(element_name="tf-text-dashboard")
|
|
234
|
+
|
|
235
|
+
def index_impl(self, ctx, experiment):
|
|
236
|
+
mapping = self._data_provider.list_tensors(
|
|
237
|
+
ctx,
|
|
238
|
+
experiment_id=experiment,
|
|
239
|
+
plugin_name=metadata.PLUGIN_NAME,
|
|
240
|
+
)
|
|
241
|
+
result = {run: [] for run in mapping}
|
|
242
|
+
for run, tag_to_content in mapping.items():
|
|
243
|
+
for tag, metadatum in tag_to_content.items():
|
|
244
|
+
md = metadata.parse_plugin_metadata(metadatum.plugin_content)
|
|
245
|
+
if not self._version_checker.ok(md.version, run, tag):
|
|
246
|
+
continue
|
|
247
|
+
result[run].append(tag)
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
@wrappers.Request.application
|
|
251
|
+
def tags_route(self, request):
|
|
252
|
+
ctx = plugin_util.context(request.environ)
|
|
253
|
+
experiment = plugin_util.experiment_id(request.environ)
|
|
254
|
+
index = self.index_impl(ctx, experiment)
|
|
255
|
+
return http_util.Respond(request, index, "application/json")
|
|
256
|
+
|
|
257
|
+
def text_impl(self, ctx, run, tag, experiment, enable_markdown):
|
|
258
|
+
all_text = self._data_provider.read_tensors(
|
|
259
|
+
ctx,
|
|
260
|
+
experiment_id=experiment,
|
|
261
|
+
plugin_name=metadata.PLUGIN_NAME,
|
|
262
|
+
downsample=self._downsample_to,
|
|
263
|
+
run_tag_filter=provider.RunTagFilter(runs=[run], tags=[tag]),
|
|
264
|
+
)
|
|
265
|
+
text = all_text.get(run, {}).get(tag, None)
|
|
266
|
+
if text is None:
|
|
267
|
+
return []
|
|
268
|
+
return [
|
|
269
|
+
process_event(d.wall_time, d.step, d.numpy, enable_markdown)
|
|
270
|
+
for d in text
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
@wrappers.Request.application
|
|
274
|
+
def text_route(self, request):
|
|
275
|
+
ctx = plugin_util.context(request.environ)
|
|
276
|
+
experiment = plugin_util.experiment_id(request.environ)
|
|
277
|
+
run = request.args.get("run")
|
|
278
|
+
tag = request.args.get("tag")
|
|
279
|
+
markdown_arg = request.args.get("markdown")
|
|
280
|
+
enable_markdown = markdown_arg != "false" # Default to enabled.
|
|
281
|
+
response = self.text_impl(ctx, run, tag, experiment, enable_markdown)
|
|
282
|
+
return http_util.Respond(request, response, "application/json")
|
|
283
|
+
|
|
284
|
+
def get_plugin_apps(self):
|
|
285
|
+
return {
|
|
286
|
+
TAGS_ROUTE: self.tags_route,
|
|
287
|
+
TEXT_ROUTE: self.text_route,
|
|
288
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Copyright 2020 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
|
+
"""Plugin that only displays a message with installation instructions."""
|
|
16
|
+
|
|
17
|
+
from tensorbored.plugins import base_plugin
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class WITRedirectPluginLoader(base_plugin.TBLoader):
|
|
21
|
+
"""Load the redirect notice iff the dynamic plugin is unavailable."""
|
|
22
|
+
|
|
23
|
+
def load(self, context):
|
|
24
|
+
try:
|
|
25
|
+
import tensorboard_plugin_wit # noqa: F401
|
|
26
|
+
|
|
27
|
+
# If we successfully load the dynamic plugin, don't show
|
|
28
|
+
# this redirect plugin at all.
|
|
29
|
+
return None
|
|
30
|
+
except ImportError:
|
|
31
|
+
return _WITRedirectPlugin(context)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _WITRedirectPlugin(base_plugin.TBPlugin):
|
|
35
|
+
"""Redirect notice pointing users to the new dynamic LIT plugin."""
|
|
36
|
+
|
|
37
|
+
plugin_name = "wit_redirect"
|
|
38
|
+
|
|
39
|
+
def get_plugin_apps(self):
|
|
40
|
+
return {}
|
|
41
|
+
|
|
42
|
+
def is_active(self):
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
def frontend_metadata(self):
|
|
46
|
+
return base_plugin.FrontendMetadata(
|
|
47
|
+
element_name="tf-wit-redirect-dashboard",
|
|
48
|
+
tab_name="What-If Tool",
|
|
49
|
+
)
|