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
tensorbored/notebook.py
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
# Copyright 2019 The TensorFlow Authors. All Rights Reserved.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ==============================================================================
|
|
14
|
+
"""Utilities for using TensorBoard in notebook contexts, like Colab.
|
|
15
|
+
|
|
16
|
+
These APIs are experimental and subject to change.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import datetime
|
|
20
|
+
import errno
|
|
21
|
+
import html
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import random
|
|
25
|
+
import shlex
|
|
26
|
+
import textwrap
|
|
27
|
+
import time
|
|
28
|
+
|
|
29
|
+
from tensorbored import manager
|
|
30
|
+
|
|
31
|
+
# Return values for `_get_context` (see that function's docs for
|
|
32
|
+
# details).
|
|
33
|
+
_CONTEXT_COLAB = "_CONTEXT_COLAB"
|
|
34
|
+
_CONTEXT_IPYTHON = "_CONTEXT_IPYTHON"
|
|
35
|
+
_CONTEXT_NONE = "_CONTEXT_NONE"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _get_context():
|
|
39
|
+
"""Determine the most specific context that we're in.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
_CONTEXT_COLAB: If in Colab with an IPython notebook context.
|
|
43
|
+
_CONTEXT_IPYTHON: If not in Colab, but we are in an IPython notebook
|
|
44
|
+
context (e.g., from running `jupyter notebook` at the command
|
|
45
|
+
line).
|
|
46
|
+
_CONTEXT_NONE: Otherwise (e.g., by running a Python script at the
|
|
47
|
+
command-line or using the `ipython` interactive shell).
|
|
48
|
+
"""
|
|
49
|
+
# In Colab, the `google.colab` module is available, but the shell
|
|
50
|
+
# returned by `IPython.get_ipython` does not have a `get_trait`
|
|
51
|
+
# method.
|
|
52
|
+
try:
|
|
53
|
+
import google.colab # noqa: F401
|
|
54
|
+
import IPython
|
|
55
|
+
except ImportError:
|
|
56
|
+
pass
|
|
57
|
+
else:
|
|
58
|
+
if IPython.get_ipython() is not None:
|
|
59
|
+
# We'll assume that we're in a Colab notebook context.
|
|
60
|
+
return _CONTEXT_COLAB
|
|
61
|
+
|
|
62
|
+
# In an IPython command line shell or Jupyter notebook, we can
|
|
63
|
+
# directly query whether we're in a notebook context.
|
|
64
|
+
try:
|
|
65
|
+
import IPython
|
|
66
|
+
except ImportError:
|
|
67
|
+
pass
|
|
68
|
+
else:
|
|
69
|
+
ipython = IPython.get_ipython()
|
|
70
|
+
if ipython is not None and ipython.has_trait("kernel"):
|
|
71
|
+
return _CONTEXT_IPYTHON
|
|
72
|
+
|
|
73
|
+
# Otherwise, we're not in a known notebook context.
|
|
74
|
+
return _CONTEXT_NONE
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def load_ipython_extension(ipython):
|
|
78
|
+
"""Deprecated: use `%load_ext tensorbored` instead.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
RuntimeError: Always.
|
|
82
|
+
"""
|
|
83
|
+
raise RuntimeError(
|
|
84
|
+
"Use '%load_ext tensorbored' instead of '%load_ext tensorbored.notebook'."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _load_ipython_extension(ipython):
|
|
89
|
+
"""Load the TensorBoard notebook extension.
|
|
90
|
+
|
|
91
|
+
Intended to be called from `%load_ext tensorbored`. Do not invoke this
|
|
92
|
+
directly.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
ipython: An `IPython.InteractiveShell` instance.
|
|
96
|
+
"""
|
|
97
|
+
_register_magics(ipython)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _register_magics(ipython):
|
|
101
|
+
"""Register IPython line/cell magics.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
ipython: An `InteractiveShell` instance.
|
|
105
|
+
"""
|
|
106
|
+
ipython.register_magic_function(
|
|
107
|
+
_start_magic,
|
|
108
|
+
magic_kind="line",
|
|
109
|
+
magic_name="tensorbored",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _start_magic(line):
|
|
114
|
+
"""Implementation of the `%tensorbored` line magic."""
|
|
115
|
+
return start(line)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def start(args_string):
|
|
119
|
+
"""Launch and display a TensorBoard instance as if at the command line.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
args_string: Command-line arguments to TensorBoard, to be
|
|
123
|
+
interpreted by `shlex.split`: e.g., "--logdir ./logs --port 0".
|
|
124
|
+
Shell metacharacters are not supported: e.g., "--logdir 2>&1" will
|
|
125
|
+
point the logdir at the literal directory named "2>&1".
|
|
126
|
+
"""
|
|
127
|
+
context = _get_context()
|
|
128
|
+
try:
|
|
129
|
+
import IPython
|
|
130
|
+
import IPython.display
|
|
131
|
+
except ImportError:
|
|
132
|
+
IPython = None
|
|
133
|
+
|
|
134
|
+
if context == _CONTEXT_NONE:
|
|
135
|
+
handle = None
|
|
136
|
+
print("Launching TensorBoard...")
|
|
137
|
+
else:
|
|
138
|
+
handle = IPython.display.display(
|
|
139
|
+
IPython.display.Pretty("Launching TensorBoard..."),
|
|
140
|
+
display_id=True,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def print_or_update(message):
|
|
144
|
+
if handle is None:
|
|
145
|
+
print(message)
|
|
146
|
+
else:
|
|
147
|
+
handle.update(IPython.display.Pretty(message))
|
|
148
|
+
|
|
149
|
+
parsed_args = shlex.split(args_string, comments=True, posix=True)
|
|
150
|
+
start_result = manager.start(parsed_args)
|
|
151
|
+
|
|
152
|
+
if isinstance(start_result, manager.StartLaunched):
|
|
153
|
+
_display(
|
|
154
|
+
port=start_result.info.port,
|
|
155
|
+
print_message=False,
|
|
156
|
+
display_handle=handle,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
elif isinstance(start_result, manager.StartReused):
|
|
160
|
+
template = (
|
|
161
|
+
"Reusing TensorBoard on port {port} (pid {pid}), started {delta} ago. "
|
|
162
|
+
"(Use '!kill {pid}' to kill it.)"
|
|
163
|
+
)
|
|
164
|
+
message = template.format(
|
|
165
|
+
port=start_result.info.port,
|
|
166
|
+
pid=start_result.info.pid,
|
|
167
|
+
delta=_time_delta_from_info(start_result.info),
|
|
168
|
+
)
|
|
169
|
+
print_or_update(message)
|
|
170
|
+
_display(
|
|
171
|
+
port=start_result.info.port,
|
|
172
|
+
print_message=False,
|
|
173
|
+
display_handle=None,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
elif isinstance(start_result, manager.StartFailed):
|
|
177
|
+
|
|
178
|
+
def format_stream(name, value):
|
|
179
|
+
if value == "":
|
|
180
|
+
return ""
|
|
181
|
+
elif value is None:
|
|
182
|
+
return "\n<could not read %s>" % name
|
|
183
|
+
else:
|
|
184
|
+
return "\nContents of %s:\n%s" % (name, value.strip())
|
|
185
|
+
|
|
186
|
+
message = (
|
|
187
|
+
"ERROR: Failed to launch TensorBoard (exited with %d).%s%s"
|
|
188
|
+
% (
|
|
189
|
+
start_result.exit_code,
|
|
190
|
+
format_stream("stderr", start_result.stderr),
|
|
191
|
+
format_stream("stdout", start_result.stdout),
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
print_or_update(message)
|
|
195
|
+
|
|
196
|
+
elif isinstance(start_result, manager.StartExecFailed):
|
|
197
|
+
the_tensorboard_binary = (
|
|
198
|
+
"%r (set by the `TENSORBOARD_BINARY` environment variable)"
|
|
199
|
+
% (start_result.explicit_binary,)
|
|
200
|
+
if start_result.explicit_binary is not None
|
|
201
|
+
else "`tensorbored`"
|
|
202
|
+
)
|
|
203
|
+
if start_result.os_error.errno == errno.ENOENT:
|
|
204
|
+
message = (
|
|
205
|
+
"ERROR: Could not find %s. Please ensure that your PATH contains "
|
|
206
|
+
"an executable `tensorbored` program, or explicitly specify the path "
|
|
207
|
+
"to a TensorBoard binary by setting the `TENSORBOARD_BINARY` "
|
|
208
|
+
"environment variable." % (the_tensorboard_binary,)
|
|
209
|
+
)
|
|
210
|
+
else:
|
|
211
|
+
message = "ERROR: Failed to start %s: %s" % (
|
|
212
|
+
the_tensorboard_binary,
|
|
213
|
+
start_result.os_error,
|
|
214
|
+
)
|
|
215
|
+
print_or_update(textwrap.fill(message))
|
|
216
|
+
|
|
217
|
+
elif isinstance(start_result, manager.StartTimedOut):
|
|
218
|
+
message = (
|
|
219
|
+
"ERROR: Timed out waiting for TensorBoard to start. "
|
|
220
|
+
"It may still be running as pid %d." % start_result.pid
|
|
221
|
+
)
|
|
222
|
+
print_or_update(message)
|
|
223
|
+
|
|
224
|
+
else:
|
|
225
|
+
raise TypeError(
|
|
226
|
+
"Unexpected result from `manager.start`: %r.\n"
|
|
227
|
+
"This is a TensorBoard bug; please report it." % start_result
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _time_delta_from_info(info):
|
|
232
|
+
"""Format the elapsed time for the given TensorBoardInfo.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
info: A TensorBoardInfo value.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
A human-readable string describing the time since the server
|
|
239
|
+
described by `info` started: e.g., "2 days, 0:48:58".
|
|
240
|
+
"""
|
|
241
|
+
delta_seconds = int(time.time()) - info.start_time
|
|
242
|
+
return str(datetime.timedelta(seconds=delta_seconds))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def display(port=None, height=None):
|
|
246
|
+
"""Display a TensorBoard instance already running on this machine.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
port: The port on which the TensorBoard server is listening, as an
|
|
250
|
+
`int`, or `None` to automatically select the most recently
|
|
251
|
+
launched TensorBoard.
|
|
252
|
+
height: The height of the frame into which to render the TensorBoard
|
|
253
|
+
UI, as an `int` number of pixels, or `None` to use a default value
|
|
254
|
+
(currently 800).
|
|
255
|
+
"""
|
|
256
|
+
_display(port=port, height=height, print_message=True, display_handle=None)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _display(port=None, height=None, print_message=False, display_handle=None):
|
|
260
|
+
"""Internal version of `display`.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
port: As with `display`.
|
|
264
|
+
height: As with `display`.
|
|
265
|
+
print_message: True to print which TensorBoard instance was selected
|
|
266
|
+
for display (if applicable), or False otherwise.
|
|
267
|
+
display_handle: If not None, an IPython display handle into which to
|
|
268
|
+
render TensorBoard.
|
|
269
|
+
"""
|
|
270
|
+
if height is None:
|
|
271
|
+
height = 800
|
|
272
|
+
|
|
273
|
+
if port is None:
|
|
274
|
+
infos = manager.get_all()
|
|
275
|
+
if not infos:
|
|
276
|
+
raise ValueError(
|
|
277
|
+
"Can't display TensorBoard: no known instances running."
|
|
278
|
+
)
|
|
279
|
+
else:
|
|
280
|
+
info = max(manager.get_all(), key=lambda x: x.start_time)
|
|
281
|
+
port = info.port
|
|
282
|
+
else:
|
|
283
|
+
infos = [i for i in manager.get_all() if i.port == port]
|
|
284
|
+
info = max(infos, key=lambda x: x.start_time) if infos else None
|
|
285
|
+
|
|
286
|
+
if print_message:
|
|
287
|
+
if info is not None:
|
|
288
|
+
message = (
|
|
289
|
+
"Selecting TensorBoard with {data_source} "
|
|
290
|
+
"(started {delta} ago; port {port}, pid {pid})."
|
|
291
|
+
).format(
|
|
292
|
+
data_source=manager.data_source_from_info(info),
|
|
293
|
+
delta=_time_delta_from_info(info),
|
|
294
|
+
port=info.port,
|
|
295
|
+
pid=info.pid,
|
|
296
|
+
)
|
|
297
|
+
print(message)
|
|
298
|
+
else:
|
|
299
|
+
# The user explicitly provided a port, and we don't have any
|
|
300
|
+
# additional information. There's nothing useful to say.
|
|
301
|
+
pass
|
|
302
|
+
|
|
303
|
+
fn = {
|
|
304
|
+
_CONTEXT_COLAB: _display_colab,
|
|
305
|
+
_CONTEXT_IPYTHON: _display_ipython,
|
|
306
|
+
_CONTEXT_NONE: _display_cli,
|
|
307
|
+
}[_get_context()]
|
|
308
|
+
return fn(port=port, height=height, display_handle=display_handle)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def _display_colab(port, height, display_handle):
|
|
312
|
+
"""Display a TensorBoard instance in a Colab output frame.
|
|
313
|
+
|
|
314
|
+
The Colab VM is not directly exposed to the network, so the Colab
|
|
315
|
+
runtime provides a service worker tunnel to proxy requests from the
|
|
316
|
+
end user's browser through to servers running on the Colab VM: the
|
|
317
|
+
output frame may issue requests to https://localhost:<port> (HTTPS
|
|
318
|
+
only), which will be forwarded to the specified port on the VM.
|
|
319
|
+
|
|
320
|
+
It does not suffice to create an `iframe` and let the service worker
|
|
321
|
+
redirect its traffic (`<iframe src="https://localhost:6006">`),
|
|
322
|
+
because for security reasons service workers cannot intercept iframe
|
|
323
|
+
traffic. Instead, we manually fetch the TensorBoard index page with an
|
|
324
|
+
XHR in the output frame, and inject the raw HTML into `document.body`.
|
|
325
|
+
|
|
326
|
+
By default, the TensorBoard web app requests resources against
|
|
327
|
+
relative paths, like `./data/logdir`. Within the output frame, these
|
|
328
|
+
requests must instead hit `https://localhost:<port>/data/logdir`. To
|
|
329
|
+
redirect them, we change the document base URI, which transparently
|
|
330
|
+
affects all requests (XHRs and resources alike).
|
|
331
|
+
"""
|
|
332
|
+
import IPython.display
|
|
333
|
+
|
|
334
|
+
shell = """
|
|
335
|
+
(async () => {
|
|
336
|
+
const url = new URL(await google.colab.kernel.proxyPort(%PORT%, {'cache': true}));
|
|
337
|
+
url.searchParams.set('tensorboardColab', 'true');
|
|
338
|
+
const iframe = document.createElement('iframe');
|
|
339
|
+
iframe.src = url;
|
|
340
|
+
iframe.setAttribute('width', '100%');
|
|
341
|
+
iframe.setAttribute('height', '%HEIGHT%');
|
|
342
|
+
iframe.setAttribute('frameborder', 0);
|
|
343
|
+
document.body.appendChild(iframe);
|
|
344
|
+
})();
|
|
345
|
+
"""
|
|
346
|
+
replacements = [
|
|
347
|
+
("%PORT%", "%d" % port),
|
|
348
|
+
("%HEIGHT%", "%d" % height),
|
|
349
|
+
]
|
|
350
|
+
for k, v in replacements:
|
|
351
|
+
shell = shell.replace(k, v)
|
|
352
|
+
script = IPython.display.Javascript(shell)
|
|
353
|
+
|
|
354
|
+
if display_handle:
|
|
355
|
+
display_handle.update(script)
|
|
356
|
+
else:
|
|
357
|
+
IPython.display.display(script)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _display_ipython(port, height, display_handle):
|
|
361
|
+
import IPython.display
|
|
362
|
+
|
|
363
|
+
frame_id = "tensorbored-frame-{:08x}".format(random.getrandbits(64))
|
|
364
|
+
shell = """
|
|
365
|
+
<iframe id="%HTML_ID%" width="100%" height="%HEIGHT%" frameborder="0">
|
|
366
|
+
</iframe>
|
|
367
|
+
<script>
|
|
368
|
+
(function() {
|
|
369
|
+
const frame = document.getElementById(%JSON_ID%);
|
|
370
|
+
const url = new URL(%URL%, window.location);
|
|
371
|
+
const port = %PORT%;
|
|
372
|
+
if (port) {
|
|
373
|
+
url.port = port;
|
|
374
|
+
}
|
|
375
|
+
frame.src = url;
|
|
376
|
+
})();
|
|
377
|
+
</script>
|
|
378
|
+
"""
|
|
379
|
+
proxy_url = os.environ.get("TENSORBOARD_PROXY_URL")
|
|
380
|
+
if proxy_url is not None:
|
|
381
|
+
# Allow %PORT% in $TENSORBOARD_PROXY_URL
|
|
382
|
+
proxy_url = proxy_url.replace("%PORT%", "%d" % port)
|
|
383
|
+
replacements = [
|
|
384
|
+
("%HTML_ID%", html.escape(frame_id, quote=True)),
|
|
385
|
+
("%JSON_ID%", json.dumps(frame_id)),
|
|
386
|
+
("%HEIGHT%", "%d" % height),
|
|
387
|
+
("%PORT%", "0"),
|
|
388
|
+
("%URL%", json.dumps(proxy_url)),
|
|
389
|
+
]
|
|
390
|
+
else:
|
|
391
|
+
replacements = [
|
|
392
|
+
("%HTML_ID%", html.escape(frame_id, quote=True)),
|
|
393
|
+
("%JSON_ID%", json.dumps(frame_id)),
|
|
394
|
+
("%HEIGHT%", "%d" % height),
|
|
395
|
+
("%PORT%", "%d" % port),
|
|
396
|
+
("%URL%", json.dumps("/")),
|
|
397
|
+
]
|
|
398
|
+
|
|
399
|
+
for k, v in replacements:
|
|
400
|
+
shell = shell.replace(k, v)
|
|
401
|
+
iframe = IPython.display.HTML(shell)
|
|
402
|
+
if display_handle:
|
|
403
|
+
display_handle.update(iframe)
|
|
404
|
+
else:
|
|
405
|
+
IPython.display.display(iframe)
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
def _display_cli(port, height, display_handle):
|
|
409
|
+
del height # unused
|
|
410
|
+
del display_handle # unused
|
|
411
|
+
message = "Please visit http://localhost:%d in a web browser." % port
|
|
412
|
+
print(message)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def list():
|
|
416
|
+
"""Print a listing of known running TensorBoard instances.
|
|
417
|
+
|
|
418
|
+
TensorBoard instances that were killed uncleanly (e.g., with SIGKILL
|
|
419
|
+
or SIGQUIT) may appear in this list even if they are no longer
|
|
420
|
+
running. Conversely, this list may be missing some entries if your
|
|
421
|
+
operating system's temporary directory has been cleared since a
|
|
422
|
+
still-running TensorBoard instance started.
|
|
423
|
+
"""
|
|
424
|
+
infos = manager.get_all()
|
|
425
|
+
if not infos:
|
|
426
|
+
print("No known TensorBoard instances running.")
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
print("Known TensorBoard instances:")
|
|
430
|
+
for info in infos:
|
|
431
|
+
template = (
|
|
432
|
+
" - port {port}: {data_source} (started {delta} ago; pid {pid})"
|
|
433
|
+
)
|
|
434
|
+
print(
|
|
435
|
+
template.format(
|
|
436
|
+
port=info.port,
|
|
437
|
+
data_source=manager.data_source_from_info(info),
|
|
438
|
+
delta=_time_delta_from_info(info),
|
|
439
|
+
pid=info.pid,
|
|
440
|
+
)
|
|
441
|
+
)
|
|
@@ -0,0 +1,266 @@
|
|
|
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
|
+
"""Provides utilities that may be especially useful to plugins."""
|
|
16
|
+
|
|
17
|
+
from google.protobuf import json_format
|
|
18
|
+
from importlib import metadata
|
|
19
|
+
from packaging import version
|
|
20
|
+
import threading
|
|
21
|
+
|
|
22
|
+
from tensorbored._vendor.bleach.sanitizer import Cleaner
|
|
23
|
+
import markdown
|
|
24
|
+
|
|
25
|
+
from tensorbored import context as _context
|
|
26
|
+
from tensorbored.backend import experiment_id as _experiment_id
|
|
27
|
+
from tensorbored.util import tb_logging
|
|
28
|
+
|
|
29
|
+
logger = tb_logging.get_logger()
|
|
30
|
+
|
|
31
|
+
_ALLOWED_ATTRIBUTES = {
|
|
32
|
+
"a": ["href", "title"],
|
|
33
|
+
"img": ["src", "title", "alt"],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_ALLOWED_TAGS = [
|
|
37
|
+
"ul",
|
|
38
|
+
"ol",
|
|
39
|
+
"li",
|
|
40
|
+
"p",
|
|
41
|
+
"pre",
|
|
42
|
+
"code",
|
|
43
|
+
"blockquote",
|
|
44
|
+
"h1",
|
|
45
|
+
"h2",
|
|
46
|
+
"h3",
|
|
47
|
+
"h4",
|
|
48
|
+
"h5",
|
|
49
|
+
"h6",
|
|
50
|
+
"hr",
|
|
51
|
+
"br",
|
|
52
|
+
"strong",
|
|
53
|
+
"em",
|
|
54
|
+
"a",
|
|
55
|
+
"img",
|
|
56
|
+
"table",
|
|
57
|
+
"thead",
|
|
58
|
+
"tbody",
|
|
59
|
+
"td",
|
|
60
|
+
"tr",
|
|
61
|
+
"th",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Cache Markdown converter to avoid expensive initialization at each
|
|
66
|
+
# call to `markdown_to_safe_html`. Cache a different instance per thread.
|
|
67
|
+
class _MarkdownStore(threading.local):
|
|
68
|
+
def __init__(self):
|
|
69
|
+
self.markdown = markdown.Markdown(
|
|
70
|
+
extensions=[
|
|
71
|
+
"markdown.extensions.tables",
|
|
72
|
+
"markdown.extensions.fenced_code",
|
|
73
|
+
]
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
_MARKDOWN_STORE = _MarkdownStore()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# Cache Cleaner to avoid expensive initialization at each call to `clean`.
|
|
81
|
+
# Cache a different instance per thread.
|
|
82
|
+
class _CleanerStore(threading.local):
|
|
83
|
+
def __init__(self):
|
|
84
|
+
self.cleaner = Cleaner(
|
|
85
|
+
tags=_ALLOWED_TAGS, attributes=_ALLOWED_ATTRIBUTES
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
_CLEANER_STORE = _CleanerStore()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def safe_html(unsafe_string):
|
|
93
|
+
"""Return the input as a str, sanitized for insertion into the DOM.
|
|
94
|
+
|
|
95
|
+
Arguments:
|
|
96
|
+
unsafe_string: A Unicode string or UTF-8--encoded bytestring
|
|
97
|
+
possibly containing unsafe HTML markup.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
A string containing safe HTML.
|
|
101
|
+
"""
|
|
102
|
+
total_null_bytes = 0
|
|
103
|
+
if isinstance(unsafe_string, bytes):
|
|
104
|
+
unsafe_string = unsafe_string.decode("utf-8")
|
|
105
|
+
return _CLEANER_STORE.cleaner.clean(unsafe_string)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def markdown_to_safe_html(markdown_string):
|
|
109
|
+
"""Convert Markdown to HTML that's safe to splice into the DOM.
|
|
110
|
+
|
|
111
|
+
Arguments:
|
|
112
|
+
markdown_string: A Unicode string or UTF-8--encoded bytestring
|
|
113
|
+
containing Markdown source. Markdown tables are supported.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
A string containing safe HTML.
|
|
117
|
+
"""
|
|
118
|
+
return markdowns_to_safe_html([markdown_string], lambda xs: xs[0])
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def markdowns_to_safe_html(markdown_strings, combine):
|
|
122
|
+
"""Convert multiple Markdown documents to one safe HTML document.
|
|
123
|
+
|
|
124
|
+
One could also achieve this by calling `markdown_to_safe_html`
|
|
125
|
+
multiple times and combining the results. Compared to that approach,
|
|
126
|
+
this function may be faster, because HTML sanitization (which can be
|
|
127
|
+
expensive) is performed only once rather than once per input. It may
|
|
128
|
+
also be less precise: if one of the input documents has unsafe HTML
|
|
129
|
+
that is sanitized away, that sanitization might affect other
|
|
130
|
+
documents, even if those documents are safe.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
markdown_strings: List of Markdown source strings to convert, as
|
|
134
|
+
Unicode strings or UTF-8--encoded bytestrings. Markdown tables
|
|
135
|
+
are supported.
|
|
136
|
+
combine: Callback function that takes a list of unsafe HTML
|
|
137
|
+
strings of the same shape as `markdown_strings` and combines
|
|
138
|
+
them into a single unsafe HTML string, which will be sanitized
|
|
139
|
+
and returned.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
A string containing safe HTML.
|
|
143
|
+
"""
|
|
144
|
+
unsafe_htmls = []
|
|
145
|
+
total_null_bytes = 0
|
|
146
|
+
|
|
147
|
+
for source in markdown_strings:
|
|
148
|
+
# Convert to utf-8 whenever we have a binary input.
|
|
149
|
+
if isinstance(source, bytes):
|
|
150
|
+
source_decoded = source.decode("utf-8")
|
|
151
|
+
# Remove null bytes and warn if there were any, since it probably means
|
|
152
|
+
# we were given a bad encoding.
|
|
153
|
+
source = source_decoded.replace("\x00", "")
|
|
154
|
+
total_null_bytes += len(source_decoded) - len(source)
|
|
155
|
+
unsafe_html = _MARKDOWN_STORE.markdown.convert(source)
|
|
156
|
+
unsafe_htmls.append(unsafe_html)
|
|
157
|
+
|
|
158
|
+
unsafe_combined = combine(unsafe_htmls)
|
|
159
|
+
sanitized_combined = _CLEANER_STORE.cleaner.clean(unsafe_combined)
|
|
160
|
+
|
|
161
|
+
warning = ""
|
|
162
|
+
if total_null_bytes:
|
|
163
|
+
warning = (
|
|
164
|
+
"<!-- WARNING: discarded %d null bytes in markdown string "
|
|
165
|
+
"after UTF-8 decoding -->\n"
|
|
166
|
+
) % total_null_bytes
|
|
167
|
+
|
|
168
|
+
return warning + sanitized_combined
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def context(environ):
|
|
172
|
+
"""Get a TensorBoard `RequestContext` from a WSGI environment.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
A `RequestContext` value.
|
|
176
|
+
"""
|
|
177
|
+
return _context.from_environ(environ)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def experiment_id(environ):
|
|
181
|
+
"""Determine the experiment ID associated with a WSGI request.
|
|
182
|
+
|
|
183
|
+
Each request to TensorBoard has an associated experiment ID, which is
|
|
184
|
+
always a string and may be empty. This experiment ID should be passed
|
|
185
|
+
to data providers.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
environ: A WSGI environment `dict`. For a Werkzeug request, this is
|
|
189
|
+
`request.environ`.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
A experiment ID, as a possibly-empty `str`.
|
|
193
|
+
"""
|
|
194
|
+
return environ.get(_experiment_id.WSGI_ENVIRON_KEY, "")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def proto_to_json(proto):
|
|
198
|
+
"""Utility method to convert proto to JSON, accounting for different version support.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
proto: The proto to convert to JSON.
|
|
202
|
+
"""
|
|
203
|
+
# Fallback for internal usage, since non third-party code doesn't really
|
|
204
|
+
# have the concept of "versions" in a monorepo. The package version chosen
|
|
205
|
+
# below is the minimum value to choose the non-deprecated kwarg to
|
|
206
|
+
# `MessageToJson`.
|
|
207
|
+
try:
|
|
208
|
+
current_version = metadata.version("protobuf")
|
|
209
|
+
except metadata.PackageNotFoundError:
|
|
210
|
+
current_version = "5.0.0"
|
|
211
|
+
if version.parse(current_version) >= version.parse("5.0.0"):
|
|
212
|
+
return json_format.MessageToJson(
|
|
213
|
+
proto,
|
|
214
|
+
always_print_fields_with_no_presence=True,
|
|
215
|
+
)
|
|
216
|
+
return json_format.MessageToJson(
|
|
217
|
+
proto,
|
|
218
|
+
including_default_value_fields=True,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class _MetadataVersionChecker:
|
|
223
|
+
"""TensorBoard-internal utility for warning when data is too new.
|
|
224
|
+
|
|
225
|
+
Specify a maximum known `version` number as stored in summary
|
|
226
|
+
metadata, and automatically reject and warn on data from newer
|
|
227
|
+
versions. This keeps a (single) bit of internal state to handle
|
|
228
|
+
logging a warning to the user at most once.
|
|
229
|
+
|
|
230
|
+
This should only be used by plugins bundled with TensorBoard, since
|
|
231
|
+
it may instruct users to upgrade their copy of TensorBoard.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def __init__(self, data_kind, latest_known_version):
|
|
235
|
+
"""Initialize a `_MetadataVersionChecker`.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
data_kind: A human-readable description of the kind of data
|
|
239
|
+
being read, like "scalar" or "histogram" or "PR curve".
|
|
240
|
+
latest_known_version: Highest tolerated value of `version`,
|
|
241
|
+
like `0`.
|
|
242
|
+
"""
|
|
243
|
+
self._data_kind = data_kind
|
|
244
|
+
self._latest_known_version = latest_known_version
|
|
245
|
+
self._warned = False
|
|
246
|
+
|
|
247
|
+
def ok(self, version, run, tag):
|
|
248
|
+
"""Test whether `version` is permitted, else complain."""
|
|
249
|
+
if 0 <= version <= self._latest_known_version:
|
|
250
|
+
return True
|
|
251
|
+
self._maybe_warn(version, run, tag)
|
|
252
|
+
return False
|
|
253
|
+
|
|
254
|
+
def _maybe_warn(self, version, run, tag):
|
|
255
|
+
if self._warned:
|
|
256
|
+
return
|
|
257
|
+
self._warned = True
|
|
258
|
+
logger.warning(
|
|
259
|
+
"Some %s data is too new to be read by this version of TensorBoard. "
|
|
260
|
+
"Upgrading TensorBoard may fix this. "
|
|
261
|
+
"(sample: run %r, tag %r, data version %r)",
|
|
262
|
+
self._data_kind,
|
|
263
|
+
run,
|
|
264
|
+
tag,
|
|
265
|
+
version,
|
|
266
|
+
)
|
|
File without changes
|
|
File without changes
|