Skip to content

Commit

Permalink
Update show based on simple web-server rather than altair_viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
jonmmease committed Mar 23, 2024
1 parent b1b9dba commit 09e8157
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 16 deletions.
75 changes: 75 additions & 0 deletions altair/utils/_show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Union, Iterable, Optional


def open_html_in_browser(
html: Union[str, bytes],
using: Union[str, Iterable[str], None] = None,
port: Optional[int] = None,
):
"""
Display an html document in a web browser without creating a temp file.
Instantiates a simple http server and uses the webbrowser module to
open the server's URL
Based on
Parameters
----------
html: str
HTML string to display
using: str or iterable of str
Name of the web browser to open (e.g. "chrome", "firefox", etc.).
If an iterable, choose the first browser available on the system.
If none, choose the system default browser.
port: int
Port to use. Defaults to a random port
"""
# Encode html to bytes
if isinstance(html, str):
html_bytes = html.encode("utf8")
else:
html_bytes = html

browser = None

if using is None:
browser = webbrowser.get(None)
else:
# normalize using to an iterable
if isinstance(using, str):
using = [using]

for browser_key in using:
try:
browser = webbrowser.get(browser_key)
if browser is not None:
break
except webbrowser.Error:
pass

if browser is None:
raise ValueError("Failed to locate a browser with name in " + str(using))

class OneShotRequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()

bufferSize = 1024 * 1024
for i in range(0, len(html_bytes), bufferSize):
self.wfile.write(html_bytes[i : i + bufferSize])

def log_message(self, format, *args):
# Silence stderr logging
pass

# Use specified port if provided, otherwise choose a random port (port value of 0)
server = HTTPServer(
("127.0.0.1", port if port is not None else 0), OneShotRequestHandler
)
browser.open("http://127.0.0.1:%s" % server.server_port)
server.handle_request()
33 changes: 17 additions & 16 deletions altair/vegalite/v5/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using_vegafusion as _using_vegafusion,
compile_with_vegafusion as _compile_with_vegafusion,
)
from ...utils._show import open_html_in_browser
from ...utils.core import DataFrameLike
from ...utils.data import DataType

Expand Down Expand Up @@ -2678,29 +2679,29 @@ def serve(
)

def show(
self, embed_opt: Optional[dict] = None, open_browser: Optional[bool] = None
self,
embed_options: Optional[dict] = None,
using: Union[str, Iterable[str], None] = None,
port: Optional[int] = None,
) -> None:
"""Show the chart in an external browser window.
"""Show the chart in an external browser tab.
This requires a recent version of the altair_viewer package.
This requires the vl-convert-python package to be installed
Parameters
----------
embed_opt : dict (optional)
embed_options : dict (optional)
The Vega embed options that control the display of the chart.
open_browser : bool (optional)
Specify whether a browser window should be opened. If not specified,
a browser window will be opened only if the server is not already
connected to a browser.
using: str or iterable of str
Name of the web browser to open (e.g. "chrome", "firefox", etc.).
If an iterable, choose the first browser available on the system.
If None, choose the system default browser.
port: int
Port to use. Defaults to a random port
"""
try:
import altair_viewer
except ImportError as err:
raise ValueError(
"'show' method requires the altair_viewer package. "
"See http://github.com/altair-viz/altair_viewer"
) from err
altair_viewer.show(self, embed_opt=embed_opt, open_browser=open_browser)
buffer = io.StringIO()
self.save(buffer, format="html", embed_options=embed_options, inline=True)
open_html_in_browser(buffer.getvalue(), using=using, port=port)

@utils.use_signature(core.Resolve)
def _set_resolve(self, **kwargs):
Expand Down

0 comments on commit 09e8157

Please sign in to comment.