-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
plugins: add sample dynamically loaded plugin (#2354)
Summary: This plugin can eventually replace the `tensorboard-plugin-example` repository; it’ll be easier for us to keep it up to date if it’s in the same repository as main TensorBoard. And it has tests! Test Plan: Run `bazel test //tensorboard/plugins/example:smoke_test` to start. Then, modify `setup.py` to remove the `entry_points` entry, and note that the test fails. Separately, modify line 42 of `__init__.py` to serve the JS under `/not_index.js` instead; the test should again fail. Test the development workflow: Follow the instructions in `README.md`. Then, edit `widgets.js` (say, add another exclamation point) and refresh to see changes live without restarting TensorBoard: ![screenshot of “Hello TensorBoard!” in example plugin][1] [1]: https://user-images.githubusercontent.com/4317806/59641012-cb700380-9114-11e9-8323-c811388df31c.png wchargin-branch: example-dynamic-plugin
- Loading branch information
Showing
7 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright 2019 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. | ||
# ============================================================================== | ||
|
||
# Generated by running `python setup.py develop`. | ||
tensorboard_plugin_example.egg-info/ | ||
|
||
# Generated by running `python setup.py bdist_wheel`. | ||
build/ | ||
dist/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Description: | ||
# TensorBoard example plugin | ||
|
||
# This plugin is not primarily built with Bazel (intentionally, to | ||
# demonstrate that Bazel is not required); we use this BUILD file only | ||
# to define integration tests for TensorBoard itself. | ||
|
||
package(default_visibility = ["//tensorboard:internal"]) | ||
|
||
licenses(["notice"]) # Apache 2.0 | ||
|
||
sh_test( | ||
name = "smoke_test", | ||
size = "large", | ||
timeout = "short", | ||
srcs = ["smoke_test.sh"], | ||
# Don't just `glob(["**"])` because `setup.py` creates artifacts | ||
# like wheels and a `tensorboard_plugin_example.egg-info` directory, | ||
# and we want to make sure that those don't interfere with the test. | ||
data = [ | ||
"setup.py", | ||
"tensorboard_plugin_example/__init__.py", | ||
"//tensorboard/pip_package", | ||
] + glob(["tensorboard_plugin_example/static/**"]), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Example plugin | ||
|
||
## Overview | ||
|
||
A sample plugin using TensorBoard’s dynamic plugin loading mechanism. It | ||
doesn’t do anything useful at the moment, but a stock TensorBoard binary | ||
(1.14+) will automatically load and display it. | ||
|
||
This code happens to live in the TensorBoard repository, but is intended | ||
to stand in for a third-party plugin developer’s separate repository. It | ||
is not built with Bazel, and it only depends on TensorBoard through its | ||
public APIs (under the assumption that TensorBoard is installed in an | ||
active virtualenv). | ||
|
||
## Usage | ||
|
||
In this directory (`tensorboard/plugins/example`), in a virtualenv with | ||
TensorBoard installed, run: | ||
|
||
``` | ||
python setup.py develop | ||
``` | ||
|
||
This will link the plugin into your virtualenv. You can edit the | ||
plugin’s source code and data files and see changes reflected without | ||
having to reinstall the plugin. | ||
|
||
Then, just run | ||
|
||
``` | ||
tensorboard --logdir /tmp/whatever | ||
``` | ||
|
||
and open TensorBoard to see the plugin’s “hello world” screen. | ||
|
||
After making changes to the Python code, you must restart TensorBoard | ||
for your changes to take effect. If your plugin reads data files at | ||
runtime, you can edit those and see changes reflected immediately. | ||
|
||
You can run | ||
|
||
``` | ||
python setup.py develop --uninstall | ||
``` | ||
|
||
to unlink the plugin from your virtualenv, after which you can also | ||
delete the `tensorboard_plugin_example.egg-info/` directory that the | ||
original `setup.py` invocation created. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Copyright 2019 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. | ||
# ============================================================================== | ||
|
||
from __future__ import absolute_import | ||
from __future__ import division | ||
from __future__ import print_function | ||
|
||
import setuptools | ||
|
||
|
||
setuptools.setup( | ||
name="tensorboard_plugin_example", | ||
version="0.1.0", | ||
description="Sample TensorBoard plugin.", | ||
packages=["tensorboard_plugin_example"], | ||
package_data={ | ||
"tensorboard_plugin_example": ["static/**"], | ||
}, | ||
entry_points={ | ||
"tensorboard_plugins": [ | ||
"example = tensorboard_plugin_example:ExamplePlugin", | ||
], | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
#!/bin/sh | ||
# Copyright 2019 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. | ||
|
||
set -eux | ||
command -v virtualenv >/dev/null | ||
|
||
workdir="$(mktemp -d)" | ||
cd "${workdir}" | ||
|
||
cleanup() { | ||
rm -r "${workdir}" | ||
} | ||
trap cleanup EXIT | ||
|
||
cp -LR "${TEST_SRCDIR}/org_tensorflow_tensorboard/tensorboard/plugins/example/" \ | ||
./example-plugin/ | ||
|
||
mkdir tensorboard-wheels | ||
tar xzvf \ | ||
"${TEST_SRCDIR}/org_tensorflow_tensorboard/tensorboard/pip_package/pip_packages.tar.gz" \ | ||
-C ./tensorboard-wheels/ | ||
|
||
virtualenv venv | ||
export VIRTUAL_ENV=venv | ||
export PATH="${PWD}/venv/bin:${PATH}" | ||
unset PYTHON_HOME | ||
|
||
# Require wheel for bdist_wheel command, and setuptools 36.2.0+ so that | ||
# env markers are handled (https://github.com/pypa/setuptools/pull/1081) | ||
pip install -qU wheel 'setuptools>=36.2.0' | ||
|
||
(cd ./example-plugin && python setup.py bdist_wheel) | ||
[ -f ./example-plugin/dist/*.whl ] # just one wheel | ||
|
||
# This plugin doesn't require TensorFlow, so we don't install it. | ||
py_major_version="$(python -c 'import sys; print(sys.version_info[0])')" | ||
pip install ./tensorboard-wheels/*py"${py_major_version}"*.whl | ||
pip install ./example-plugin/dist/*.whl | ||
|
||
# Test tensorboard + tensorboard_plugin_example integration. | ||
mkfifo pipe | ||
tensorboard --port=0 --logdir=smokedir 2>pipe & | ||
perl -ne 'print STDERR;/http:.*:(\d+)/ and print $1.v10 and exit 0' <pipe >port | ||
curl -fs "http://localhost:$(cat port)/data/plugins_listing" >plugins_listing | ||
grep -F '"example":' plugins_listing | ||
grep -F '"/data/plugin/example/index.js"' plugins_listing | ||
curl -fs "http://localhost:$(cat port)/data/plugin/example/index.js" >index.js | ||
diff -u example-plugin/tensorboard_plugin_example/static/index.js index.js | ||
kill $! |
60 changes: 60 additions & 0 deletions
60
tensorboard/plugins/example/tensorboard_plugin_example/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Copyright 2019 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. | ||
# ============================================================================== | ||
"""A sample plugin to demonstrate dynamic loading.""" | ||
|
||
from __future__ import absolute_import | ||
from __future__ import division | ||
from __future__ import print_function | ||
|
||
import os | ||
|
||
from tensorboard.plugins import base_plugin | ||
import werkzeug | ||
from werkzeug import wrappers | ||
|
||
|
||
class ExamplePlugin(base_plugin.TBPlugin): | ||
plugin_name = 'example' | ||
|
||
def __init__(self, context): | ||
# A real plugin would likely save the `context.multiplexer` and/or | ||
# `context.db_connection_provider` attributes for later use, but we | ||
# don't actually need any of that. | ||
pass | ||
|
||
def is_active(self): | ||
return True | ||
|
||
def get_plugin_apps(self): | ||
return { | ||
"/index.js": self._serve_js, | ||
} | ||
|
||
def frontend_metadata(self): | ||
return super(ExamplePlugin, self).frontend_metadata()._replace( | ||
es_module_path="/index.js", | ||
) | ||
|
||
@wrappers.Request.application | ||
def _serve_js(self, request): | ||
del request # unused | ||
filepath = os.path.join(os.path.dirname(__file__), "static", "index.js") | ||
with open(filepath) as infile: | ||
contents = infile.read() | ||
return werkzeug.Response( | ||
contents, | ||
status=200, | ||
content_type="application/javascript", | ||
) |
20 changes: 20 additions & 0 deletions
20
tensorboard/plugins/example/tensorboard_plugin_example/static/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright 2019 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. | ||
// ============================================================================== | ||
|
||
export function render() { | ||
const msg = document.createElement("span"); | ||
msg.innerText = "Hello TensorBoard!"; | ||
document.body.appendChild(msg); | ||
} |