Git Product home page Git Product logo

altair_saver's Introduction

Altair Saver

This repo is no longer maintained. Since Altair 5.2, the functionality of Altair Saver is now available in Altair via the vl-convert package. Most of the functionality has been available since 5.0, and the main addition in 5.2 was PDF export. See the docs on how to save charts for more details


github actions github actions code style black Open in Colab

This packge provides extensions to Altair for saving charts to a variety of output types. Supported output formats are:

  • .json/.vl.json: Vega-Lite JSON specification
  • .vg.json: Vega JSON specification
  • .html: HTML output
  • .png: PNG image
  • .svg: SVG image
  • .pdf: PDF image

Usage

The altair_saver library has a single public function, altair_saver.save(). Given an Altair chart named chart, you can use it as follows:

from altair_saver import save

save(chart, "chart.vl.json")              # Vega-Lite JSON specification
save(chart, "chart.vg.json")              # Vega JSON specification
save(chart, "chart.html")                 # HTML document
save(chart, "chart.html", inline=True)    # HTML document with all JS code included inline
save(chart, "chart.png")                  # PNG Image
save(chart, "chart.svg")                  # SVG Image
save(chart, "chart.pdf")                  # PDF Image

Renderer

Additionally, altair_saver provides an Altair Renderer entrypoint that can display the above outputs directly in Jupyter notebooks. For example, you can specify a vega-lite mimetype (supported by JupyterLab, nteract, and other platforms) with a PNG fallback for other frontends as follows:

alt.renderers.enable('altair_saver', fmts=['vega-lite', 'png'])

Installation

The altair_saver package can be installed with:

$ pip install altair_saver

Saving as vl.json and as html requires no additional setup.

To install with conda, use

$ conda install -c conda-forge altair_saver

The conda package installs the NodeJS dependencies described below, so charts can be saved to png, svg, and pdf without additional setup.

Additional Requirements

Output to png, svg, and pdf requires execution of Javascript code, which altair_saver can do via one of two backends.

Selenium

The selenium backend supports the following formats:

  • .vg.json
  • .png
  • .svg.

To be used, it requires the Selenium Python package, and a properly configured installation of either chromedriver or geckodriver.

On Linux systems, this can be setup as follows:

$ pip install selenium
$ apt-get install chromium-chromedriver

Using conda, the required packages can be installed as follows:

$ conda install -c conda-forge python-chromedriver-binary

The conda approach additionally requires separate installation of a compatible version of Google Chrome, which cannot be done via conda.

Selenium supports other browsers as well, but altair-saver is currently only tested with Chrome.

NodeJS

The nodejs backend supports the following formats:

  • .vg.json
  • .png
  • .svg
  • .pdf

It requires NodeJS, along with the vega-lite, vega-cli, and canvas packages.

First install NodeJS either by direct download or via a package manager, and then use the npm tool to install the required packages:

$ npm install vega-lite vega-cli canvas

Using conda, node and the required packages can be installed as follows:

$ conda install -c conda-forge vega-cli vega-lite-cli

These packages are included automatically when installing altair_saver via conda-forge.

altair_saver's People

Contributors

boydgreenfield avatar ivirshup avatar jakevdp avatar joelostblom avatar kannes avatar xhochy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

altair_saver's Issues

Appears with a lot of errors

Hello,

just wondering, why it works so unstable? I like the altair package, but saving is quite painful. The selenium backend gives an error with newer versions, the workaround with 4.2.0 version works not every time. For example, now it gives me the error about MaxRetries.

If I use the other backend, then

vega-lite.js:740
      deepMerge_(dest, s ?? {});
                          ^

SyntaxError: Unexpected token '?'

or some other similar errors.

Overall, saving in any backend with either pdf or svg gives different errors.

Garbled characters when exporting to PNG format

Chinese fonts are displayed normally in HTML format. Garbled characters when exporting to PNG format.

`import pandas as pd
import altair as alt
from altair_saver import save

df = pd.DataFrame({"x":[0.5,1,2],
"y" : [0.5,1,2],
"text": ["你好", "**", "明天"]})

pic = alt.Chart(df).mark_text().encode(
x = "x",
y= 'y',
text ='text'
)
save(pic, "text.png")`

Missing chromedriver.exe on Windows results in cryptic JSONDecodeError error

I tried to run code I had written Linux in a Windows & Conda environment. When trying to save a chart to SVG, I got a JSONDecodeError:

---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
<ipython-input-23-be5f964fdbaf> in <module>
     12 
     13     chart = make_plot(df, foo, bar)
---> 14     chart.save(filepath, format="svg")
     15     ...
     16     

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair\vegalite\v4\api.py in save(self, fp, format, override_data_transformer, scale_factor, vegalite_version, vega_version, vegaembed_version, **kwargs)
    474         if override_data_transformer:
    475             with data_transformers.disable_max_rows():
--> 476                 result = save(**kwds)
    477         else:
    478             result = save(**kwds)

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair\utils\save.py in save(chart, fp, vega_version, vegaembed_version, format, mode, vegalite_version, embed_options, json_kwds, webdriver, scale_factor, **kwargs)
    110         write_file_or_filename(fp, mimebundle["text/html"], mode="w")
    111     elif format in ["png", "svg"]:
--> 112         mimebundle = spec_to_mimebundle(
    113             spec=spec,
    114             format=format,

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair\utils\mimebundle.py in spec_to_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, **kwargs)
     58                 "see http://github.com/altair-viz/altair_saver/".format(fmt=format)
     59             )
---> 60         return altair_saver.render(spec, format, mode=mode, **kwargs)
     61     if format == "html":
     62         html = spec_to_html(

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair_saver\_core.py in render(chart, fmts, mode, embed_options, method, **kwargs)
    255         Saver = _select_saver(method, mode=mode, fmt=fmt)
    256         saver = Saver(spec, mode=mode, embed_options=embed_options, **kwargs)
--> 257         mimebundle.update(saver.mimebundle(fmt))
    258 
    259     return mimebundle

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair_saver\savers\_saver.py in mimebundle(self, fmts)
     88                 vegalite_version=self._package_versions["vega-lite"],
     89             )
---> 90             bundle[mimetype] = self._serialize(fmt, "mimebundle")
     91         return bundle
     92 

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair_saver\savers\_node.py in _serialize(self, fmt, content_type)
    112 
    113         if self._mode == "vega-lite":
--> 114             spec = self._vl2vg(spec)
    115 
    116         if fmt == "vega":

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\site-packages\altair_saver\savers\_node.py in _vl2vg(self, spec)
     66             [vl2vg], input=vl_json, stderr_filter=self._stderr_filter
     67         )
---> 68         return json.loads(vg_json)
     69 
     70     def _vg2png(self, spec: JSONDict) -> bytes:

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\json\__init__.py in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    355             parse_int is None and parse_float is None and
    356             parse_constant is None and object_pairs_hook is None and not kw):
--> 357         return _default_decoder.decode(s)
    358     if cls is None:
    359         cls = JSONDecoder

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\json\decoder.py in decode(self, s, _w)
    335 
    336         """
--> 337         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338         end = _w(s, end).end()
    339         if end != len(s):

~\AppData\Local\Continuum\anaconda3\envs\rwa\lib\json\decoder.py in raw_decode(self, s, idx)
    353             obj, end = self.scan_once(s, idx)
    354         except StopIteration as err:
--> 355             raise JSONDecodeError("Expecting value", s, err.value) from None
    356         return obj, end

JSONDecodeError: Expecting value: line 2 column 1 (char 2)

This seems to have been caused by a missing setup for the chromedriver.

It worked once I had installed and set up everything, validating by using an example that should open a Browser:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://jupyter.org/')

For me the steps were:

Now it works.

Posting here so future MEs will find it via Google.

Windows support for Altair Saver via NodeJS backend

Hi there,

I tried to install and configure Altair Saver using the NodeJS backend on Windows 10, but I'm getting an error when trying to save from JupyterLab running via Miniconda on Git Bash. I have NodeJS installed and I'm able to install the vega-lite and vega-cli packages. This puts a bunch of scripts in my ~/.npm-packages/bin folder, so far so good:

$ ls ~/.npm-packages/bin
vg2pdf      vg2png      vg2svg      vl2pdf      vl2png      vl2svg      vl2vg
vg2pdf.cmd  vg2png.cmd  vg2svg.cmd  vl2pdf.cmd  vl2png.cmd  vl2svg.cmd  vl2vg.cmd

I have a simple Altair Chart that I'm trying to save (it displays fine inside JupyterLab), and it returns this error message and stack trace:

(not in PATH env variable)
TypeError: Cannot read property 'getContext' of null
    at resize (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-scenegraph\build\vega-scenegraph.js:3678:28)
    at CanvasRenderer.prototype$6.resize (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-scenegraph\build\vega-scenegraph.js:3727:5)
    at CanvasRenderer.prototype$4.initialize (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-scenegraph\build\vega-scenegraph.js:3307:17)
    at CanvasRenderer.prototype$6.initialize (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-scenegraph\build\vega-scenegraph.js:3722:28)
    at initializeRenderer (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-view\build\vega-view.js:649:8)
    at renderHeadless (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-view\build\vega-view.js:772:12)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async View.renderToCanvas [as toCanvas] (C:\Users\Jonathan.Yu\AppData\Local\Yarn\Data\global\node_modules\vega-view\build\vega-view.js:807:15)

I installed using yarn global so that might be part of the problem? I'd be happy to debug and contribute a patch but would need a pointer to the relevant bits of code.

is `alt.renderers.enable('altair_saver', ['vega-lite', 'png'])` on README outdated?

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-53-092c93936f9f> in <module>
----> 1 altair.renderers.enable('altair_saver', ['vega-lite', 'png'])

TypeError: enable() takes from 1 to 2 positional arguments but 3 were given

https://github.com/altair-viz/altair/blob/582b590a29dfbfc06aad27be79371f98a763a0aa/altair/utils/plugin_registry.py#L156

Specify geckodriver for saving once in file?

Is there anyway to set the webdriver parameter one time so that all save("chart.png") calls behave like save("chart.png", webdriver="firefox")?

I couldn't find this parameter documented on the site or the repo.

Further, my geckodriver log mentions "Missing chrome" which may be related:

1659451959509	geckodriver	INFO	Listening on 127.0.0.1:43221
1659451960014	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "34867" "-no-remote" "-profile" "/tmp/rust_mozprofile6nAKtY"
*** You are running in headless mode.
1659451960547	Marionette	INFO	Marionette enabled
1659451960551	Marionette	INFO	Listening on port 37075
WebDriver BiDi listening on ws://localhost:34867
1659451960662	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofile6nAKtY/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:34867/devtools/browser/4ffd4eb8-86d2-4a8b-bf91-65d49095b992
1659451962223	Marionette	INFO	Stopped listening on port 37075
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
console.error: services.settings: 
  main/query-stripping Signature failed  TypeError: NetworkError: Network request failed
1659452043194	geckodriver	INFO	Listening on 127.0.0.1:47067
1659452043700	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "51391" "-no-remote" "-profile" "/tmp/rust_mozprofileEklnxv"
*** You are running in headless mode.
1659452044176	Marionette	INFO	Marionette enabled
1659452044180	Marionette	INFO	Listening on port 34497
WebDriver BiDi listening on ws://localhost:51391
1659452044288	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofileEklnxv/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:51391/devtools/browser/069b6eab-ad66-4a53-ae7d-41ec3cbda8b7
1659452045937	Marionette	INFO	Stopped listening on port 34497
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
1659452188125	geckodriver	INFO	Listening on 127.0.0.1:55165
1659452188631	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "55793" "-no-remote" "-profile" "/tmp/rust_mozprofilefb38UI"
*** You are running in headless mode.
1659452189096	Marionette	INFO	Marionette enabled
1659452189100	Marionette	INFO	Listening on port 46573
WebDriver BiDi listening on ws://localhost:55793
1659452189210	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofilefb38UI/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:55793/devtools/browser/638b53b0-8cc4-474b-9cc6-8e83ebf0dce1
1659452190733	Marionette	INFO	Stopped listening on port 46573
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
console.error: services.settings: 
  main/query-stripping Signature failed  TypeError: NetworkError: Network request failed
Sandbox: Unexpected EOF, op 0 flags 00 path /proc/cpuinfo
1659452385361	geckodriver	INFO	Listening on 127.0.0.1:49513
1659452385871	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "56497" "-no-remote" "-profile" "/tmp/rust_mozprofilema6R3H"
*** You are running in headless mode.
1659452386330	Marionette	INFO	Marionette enabled
1659452386334	Marionette	INFO	Listening on port 34623
WebDriver BiDi listening on ws://localhost:56497
1659452386443	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofilema6R3H/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:56497/devtools/browser/09789d9b-ed9e-46b6-b257-b3f78924db41
1659452387506	Marionette	INFO	Stopped listening on port 34623
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
1659453992531	geckodriver	INFO	Listening on 127.0.0.1:57613
1659453993039	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "60641" "-no-remote" "-profile" "/tmp/rust_mozprofileuaYa4t"
*** You are running in headless mode.
1659453993475	Marionette	INFO	Marionette enabled
1659453993482	Marionette	INFO	Listening on port 40759
WebDriver BiDi listening on ws://localhost:60641
1659453993640	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofileuaYa4t/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:60641/devtools/browser/9014982d-114f-4882-8c88-4c9ade1562ea
1659453995305	Marionette	INFO	Stopped listening on port 40759
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
console.error: services.settings: 
  main/query-stripping Signature failed  TypeError: NetworkError: Network request failed
console.error: Region.jsm: "Error fetching region" (new TypeError("NetworkError when attempting to fetch resource.", ""))
console.error: Region.jsm: "Failed to fetch region" (new Error("NO_RESULT", "resource://gre/modules/Region.jsm", 421))
JavaScript error: resource://gre/modules/TerminatorTelemetry.jsm, line 69: AbortError: IOUtils: Shutting down and refusing additional I/O tasks
1659457011204	geckodriver	INFO	Listening on 127.0.0.1:60811
1659457011711	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "48955" "-no-remote" "-profile" "/tmp/rust_mozprofileseoGNF"
*** You are running in headless mode.
1659457012161	Marionette	INFO	Marionette enabled
1659457012165	Marionette	INFO	Listening on port 45167
WebDriver BiDi listening on ws://localhost:48955
1659457012272	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofileseoGNF/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:48955/devtools/browser/ced63948-e904-46d0-a0f7-7a68ac7557d4
1659457013687	Marionette	INFO	Stopped listening on port 45167
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
console.error: services.settings: 
  main/query-stripping Signature failed  TypeError: NetworkError: Network request failed
console.error: services.settings: 
  main/quicksuggest Signature failed  TypeError: NetworkError: Network request failed
Sandbox: Unexpected EOF, op 0 flags 00 path /proc/cpuinfo
1659457159814	geckodriver	INFO	Listening on 127.0.0.1:33957
1659457160322	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "34269" "-no-remote" "-profile" "/tmp/rust_mozprofileXjEB2g"
*** You are running in headless mode.
1659457160781	Marionette	INFO	Marionette enabled
1659457160786	Marionette	INFO	Listening on port 37547
WebDriver BiDi listening on ws://localhost:34269
1659457160894	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofileXjEB2g/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:34269/devtools/browser/9b8aa728-eada-48e1-a0a7-8d19b298e1bb
1659457162755	Marionette	INFO	Stopped listening on port 37547
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
JavaScript error: resource://services-settings/RemoteSettingsWorker.jsm, line 145: Error: Shutdown, aborting read-only worker requests.
1659457264832	geckodriver	INFO	Listening on 127.0.0.1:57477
1659457265340	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "51395" "-no-remote" "-profile" "/tmp/rust_mozprofileOJUtTt"
*** You are running in headless mode.
1659457265837	Marionette	INFO	Marionette enabled
1659457265842	Marionette	INFO	Listening on port 35329
WebDriver BiDi listening on ws://localhost:51395
1659457266111	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofileOJUtTt/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:51395/devtools/browser/7b77ea9a-6b41-47fe-af4a-445c2f2c42fe
1659457267688	Marionette	INFO	Stopped listening on port 35329
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
JavaScript error: resource://services-settings/RemoteSettingsWorker.jsm, line 145: Error: Shutdown, aborting read-only worker requests.
1659463173791	geckodriver	INFO	Listening on 127.0.0.1:54749
1659463174297	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "42975" "-no-remote" "-profile" "/tmp/rust_mozprofilej4tqod"
*** You are running in headless mode.
1659463174720	Marionette	INFO	Marionette enabled
1659463174724	Marionette	INFO	Listening on port 45119
WebDriver BiDi listening on ws://localhost:42975
1659463174940	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofilej4tqod/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:42975/devtools/browser/b525e182-bea8-4c47-a467-4b3215419a7f
1659463177937	Marionette	INFO	Stopped listening on port 45119
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
JavaScript error: resource://services-settings/Utils.jsm, line 240: TypeError: NetworkError: Network request failed
1659463634209	geckodriver	INFO	Listening on 127.0.0.1:47755
1659463634716	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "--remote-debugging-port" "42669" "-no-remote" "-profile" "/tmp/rust_mozprofile3v8pXD"
*** You are running in headless mode.
1659463635148	Marionette	INFO	Marionette enabled
1659463635152	Marionette	INFO	Listening on port 38193
WebDriver BiDi listening on ws://localhost:42669
1659463635299	RemoteAgent	WARN	TLS certificate errors will be ignored for this session
[GFX1-]: RenderCompositorSWGL failed mapping default framebuffer, no dt
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new NotFoundError("Could not open the file at /tmp/rust_mozprofile3v8pXD/search.json.mozlz4", (void 0)))
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
DevTools listening on ws://localhost:42669/devtools/browser/91f3b26e-c2ba-4fd6-bd85-0c92f62c444f
1659463638521	Marionette	INFO	Stopped listening on port 38193
Missing chrome or resource URL: resource://gre/modules/UpdateListener.jsm
Missing chrome or resource URL: resource://gre/modules/UpdateListener.sys.mjs
console.error: "Error during quit-application-granted: [Exception... \"File error: Not found\"  nsresult: \"0x80520012 (NS_ERROR_FILE_NOT_FOUND)\"  location: \"JS frame :: resource:///modules/BrowserGlue.jsm :: _onQuitApplicationGranted/tasks< :: line 2006\"  data: no]"
JavaScript error: resource://services-settings/Utils.jsm, line 240: TypeError: NetworkError: Network request failed
Sandbox: Unexpected EOF, op 0 flags 00 path /proc/cpuinfo

altair               4.2.0
altair-data-server   0.4.1
altair-saver         0.5.0
altair-viewer        0.4.0
selenium             4.2.0
geckodriver          0.30.0

UnicodeEncodeError: 'charmap' codec can't encode character '\u2265'

Hi Jake!

Thank you sooooooo much for your work on, well, everything. Here, in particular on altair ❤️

I was thrilled to discover altair_saver, as I just happened to need a way of saving plots to HTML with the javascript dependencies inline: altair_saver.save(chart, "boom.html", inline=True)!

Problem

At first try, it fails on my machine:

>>> import altair
>>> import altair_saver
>>> import vega_datasets
>>> 
>>> chart = altair.Chart(vega_datasets.data.cars.url).mark_point().encode(
>>>     x='Horsepower:Q',
>>>     y='Miles_per_Gallon:Q',
>>>     color='Origin:N'
>>> )
>>> altair_saver.save(chart, "cars.html", inline=True)

---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-2-526fd86c54d9> in <module>
----> 1 altair_saver.save(chart, "cars.html", inline=True)

~\AppData\Local\Continuum\miniconda3\envs\plotter\lib\site-packages\altair_saver\_core.py in save(chart, fp, fmt, mode, method, **kwargs)
     75     saver = Saver(spec, mode=mode, **kwargs)
     76 
---> 77     saver.save(fp=fp, fmt=fmt)
     78 
     79 

~\AppData\Local\Continuum\miniconda3\envs\plotter\lib\site-packages\altair_saver\savers\_saver.py in save(self, fp, fmt)
     92         elif isinstance(content, str):
     93             with maybe_open(fp, "w") as f:
---> 94                 f.write(content)
     95         elif isinstance(content, bytes):
     96             with maybe_open(fp, "wb") as f:

~\AppData\Local\Continuum\miniconda3\envs\plotter\lib\encodings\cp1252.py in encode(self, input, final)
     17 class IncrementalEncoder(codecs.IncrementalEncoder):
     18     def encode(self, input, final=False):
---> 19         return codecs.charmap_encode(input,self.errors,encoding_table)[0]
     20 
     21 class IncrementalDecoder(codecs.IncrementalDecoder):

UnicodeEncodeError: 'charmap' codec can't encode character '\u2265' in position 246219: character maps to <undefined>
  • '\u2265' is the innocent-looking GREATER-THAN OR EQUAL TO sign, ≥
    • It seems to come from the source for vega.js:
    (Pdb)  input.find("// vega.js v")
    74
    (Pdb)  input.find("// vega-lite.js v")
    481314
    
  • The encoding happens in a module named "cp1252.py", which seems to indicate the Windows 1252 encoding
    • How my machine ends up thinking 1252 is the way to go is beyond me: I checked sys.getdefaultencoding() and got utf-8.

Context

  • altair 4.0.1
  • altair_saver 0.1.0
  • vega_datasets 0.8.0

conda-installed python 3.7.6
Windows 10

Monkeypatching solution

I found that I could "solve" the problem (i.e., successfully save a usable html) by monkeypatching altair_saver.savers._saver.maybe_open to explicitly use utf-8, like so:

import contextlib
from typing import Union, Iterator, IO

@contextlib.contextmanager
def maybe_open(fp: Union[IO, str], mode: str = "w") -> Iterator[IO]:
    """Write to string or file-like object"""
    if isinstance(fp, str):
        with open(fp, mode, encoding="UTF-8") as f:    # --------- I added the encoding kwarg
            yield f
    elif isinstance(fp, io.TextIOBase) and "b" in mode:
        raise ValueError("File expected to be opened in binary mode.")
    elif isinstance(fp, io.BufferedIOBase) and "b" not in mode:
        raise ValueError("File expected to be opened in text mode")
    else:
        yield fp
        
from altair_saver.savers import _saver
_saver.maybe_open = maybe_open

I know far too little about HTML encodings to tell if this is a generally good idea, so... draw your own conclusions 😉

Altair Renderer entrypoint fails

From the README:

alt.renderers.enable('altair_saver', ['vega-lite', 'png'])

When I ran this I got the error

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-39-179b5d086901> in <module>
----> 1 alt.renderers.enable('altair_saver', ['vega-lite', 'png'])

TypeError: enable() takes from 1 to 2 positional arguments but 3 were given

versions

# packages in environment 
#
# Name                    Version                   Build  Channel
altair                    4.1.0                      py_1    conda-forge
altair_data_server        0.4.1                      py_0    conda-forge
altair_saver              0.5.0                      py_0    conda-forge
altair_viewer             0.3.0                      py_0    conda-forge

Saving to svg using Node on Windows

Hi, thank you for your amazing work!

I tried generating svg images using NodeJS, but got an ValueError: Unsupported format: 'svg'.
After removing a newline character from the npm_bin path, it worked.
In savers/_node.py, I changed
return check_output_with_stderr(cmd).decode()
for
return check_output_with_stderr(cmd).decode().rstrip('\n')

I don't really understand where that newline came from, but I'm using Windows 10.
Hope this helps!

Saving to .svg on Windows 10: JSONDecodeError

Hi there, even after reading the instructions multiple times, I'm still not able to save a geoshapes-based chart to .svg with altair_saver on Windows 10 (executing in VSCode-Python). I'm getting this error reported elsewhere when calling both chart.save("test.svg", webdriver='firefox') as well as chart.save("test.svg", webdriver='chrome'):

JSONDecodeError: Expecting value: line 2 column 1 (char 2)

My relevant package versions are

altair 4.1.0
altair_saver 0.5.0
vega 3.5.0
vega-cli 5.17.0             
vega-lite-cli 4.17.0               
selenium 3.141.0
python-chromedriver-binary 92.0.4515.43.0

My Chrome browser is version 92.0.4515.131.

I added the path to a chromedriver.exe to the Path environment variable (in the system variables). Calling print(os.environ) from within my Python environment also shows this path clearly being listed.

I know this package hasn't been tested for Windows, but Is there anything else I could try to get this to work?

Thanks a million!

Possible Windows-only error on save() to PNG

Getting an error using Altair saver when I am trying to save a chart to a PNG in Jupyter Notebook: ValueError: Unsupported format: 'png'

Answer here on Stack Overflow suggested filing a bug report.
image

System info:

Windows 10
conda 4.8.2
Python 3.8.3
altair 4.1.0 py_1 conda-forge
altair_saver 0.1.0 py_0 conda-forge
vega 3.4.0 py38h32f6830_0 conda-forge
selenium 3.141.0 py38h9de7a3e_1001 conda-forge

Example Code:

import pandas as pd
import altair as alt
from altair_saver import save
alt.renderers.enable('default'); # if in jupyter, ; to suppress output
alt.renderers.enable('altair_saver', fmts=['vega-lite', 'png']);

mytaskbars = pd.DataFrame([
    {"task": "Task1a", "start": '2020-06-01', "end": '2020-09-30', "color": 'royalblue'},
    {"task": "Task1b", "start": '2020-06-01', "end": '2021-03-31', "color": 'deepskyblue'},
    {"task": "Task2", "start": '2020-06-01', "end": '2021-03-31', "color": 'red'},
    ])
    
mytaskbars["start"] = pd.to_datetime(mytaskbars["start"])
mytaskbars["end"] = pd.to_datetime(mytaskbars["end"])
    
    
chart = alt.Chart(mytaskbars).mark_bar(opacity=0.7).encode(
    x=alt.X('start', axis=alt.Axis(title='Date', labelAngle=-45, format = ("%b %Y"))),
     x2 = 'end',
    y=alt.Y('task', axis=alt.Axis(title=None)),
    color = alt.Color('color:N', scale = None)
    )
    
save(chart, "chart_202006.png")
chart

Error Message:

ValueError                                Traceback (most recent call last)
<ipython-input-3-13a284c2aca9> in <module>
     19     )
     20 
---> 21 save(chart, "chart_202006.png")
     22 chart

~\anaconda3\envs\geospat_env\lib\site-packages\altair_saver\_core.py in save(chart, fp, fmt, mode, method, **kwargs)
     60     """
     61     if method is None:
---> 62         Saver = _get_saver_for_format(fmt=fmt, fp=fp)
     63     elif isinstance(method, type):
     64         Saver = method

~\anaconda3\envs\geospat_env\lib\site-packages\altair_saver\_core.py in _get_saver_for_format(fmt, fp)
     28         if fmt in s.valid_formats and s.enabled():
     29             return s
---> 30     raise ValueError(f"Unsupported format: {fmt!r}")
     31 
     32 

ValueError: Unsupported format: 'png'

vega2png, vega2pdf, .. CLI commands

for command line tools producing vega json commands such as vega2png could be useful to produce visualizations from piped input.

Something like this:

import sys 
import altair as alt
from altair_saver import save
infile = sys.stdin() if sys.argv[1]=="-" else open(sys.argv[1])
chart = alt.Chart.from_json(infile.read())
save(chart,sys.argv[2])

curl http://... | vega2png - output.png would seem quite useful. Is this in scope for this package?

NodeJS backend not rendering themes to SVG

Possibly related to #21 #22 and #81 - but the nodeJS backend is not properly rendering themes applied to SVG outputs, both when using chart.save('output.svg'), chart.save('output.svg', embed_options={'renderer':'svg'}), and save(chart, "output.svg")`. It renders fine in Jupyter and vega-embed.

SVG export and custom mark_point() shapes

I'm experiencing two issues which I believe are related to how altair_saver exports SVG strings:

  1. When I export the SVG string of a mark_circle chart and directly copy/paste the mark-symbol path into a mark_point chart with custom pointer shape, the resulting figure appears different from the original

  2. When I export the SVG of the second chart, the mark-symbol path contains many NaNs and appears to have a different structure than the input path

A simple example:

import numpy as np
import pandas as pd
import altair as alt
import altair_saver

# create a simple scatter plot with a single circulat point at the origin
df = pd.DataFrame({'x':[0], 'y':[0]}, index=[0])
circleChart = alt.Chart(df, background='none').mark_circle().encode(
    x=alt.X('x', axis=None),
    y=alt.Y('y', axis=None)
).configure_view(strokeOpacity=0)

display(circleChart)

# export to SVG string
altair_saver.save(circleChart, fmt='svg')

Result:
image

<svg class="marks" width="210" height="210" viewBox="0 0 210 210" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(5,5)"><g class="mark-group role-frame root"><g transform="translate(0,0)"><path class="background" d="M0.5,0.5h200v200h-200Z" style="fill: none; stroke: #ddd; stroke-opacity: 0;"></path><g><g class="mark-symbol role-mark marks"><path transform="translate(100,100)" d="M2.7386127875258306,0A2.7386127875258306,2.7386127875258306,0,1,1,-2.7386127875258306,0A2.7386127875258306,2.7386127875258306,0,1,1,2.7386127875258306,0" style="fill: #4c78a8; stroke-width: 2; opacity: 0.7;"></path></g></g><path class="foreground" d="" style="display: none; fill: none;"></path></g></g></g></svg>'

Take the exact SVG from the mark-symbol and use it to define a custom marker:

# the exact mark-symbol SVG path produced by the first example
customSymbolSvg = '<path transform="translate(200,150)" d="M2.7386127875258306,0A2.7386127875258306,2.7386127875258306,0,1,1,-2.7386127875258306,0A2.7386127875258306,2.7386127875258306,0,1,1,2.7386127875258306,0" style="fill: #4c78a8; stroke-width: 2; opacity: 0.7;"></path>'

# create a new scatter plot with point shape defined by SVG string
customSymbolChart = alt.Chart(df, background='none').mark_point(
    shape=customSymbolSvg,
    fill='#4c78a8',
    stroke=None
).encode(
    x=alt.X('x', axis=None), 
    y=alt.Y('y', axis=None)
).configure_view(strokeOpacity=0)

display(customSymbolChart)

# export to SVG string
altair_saver.save(customSymbolChart, fmt='svg')

Result:
image

'<svg class="marks" width="410" height="310" viewBox="0 0 410 310" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(5,5)"><g class="mark-group role-frame root"><g transform="translate(0,0)"><path class="background" d="M0.5,0.5h400v300h-400Z" style="fill: none; stroke: #ddd; stroke-opacity: 0;"></path><g><g class="mark-symbol role-mark marks"><path transform="translate(200,150)" d="QNaN,NaN,NaN,NaNLNaN,NaNQNaN,NaN,NaN,NaNCNaN,NaN,NaN,NaN,NaN,NaNMNaN,NaNQNaN,NaN,NaN,NaNCNaN,NaN,NaN,NaN,NaN,NaNLNaN,NaNQNaN,NaN,NaN,NaNM7.5,0C7.5,4.142135623730951,4.142135623730952,7.5,4.592425496802575e-16,7.5C-4.14213562373095,7.5,-7.499999999999999,4.142135623730952,-7.5,9.18485099360515e-16CNaN,NaN,7.5,NaN,7.5,NaNQNaN,NaN,7.5,NaNL7.5,NaNL7.5,NaNL7.5,NaNC221.11179742701478,NaN,7.5,NaN,8.5,NaNCNaN,NaN,NaN,NaN,NaN,NaNQNaN,NaN,NaN,NaNQNaN,NaN,NaN,NaNLNaN,NaNCNaN,NaN,NaN,NaN,NaN,NaNQNaN,NaN,NaN,NaNQNaN,NaN,NaN,NaNLNaN,NaN" style="fill: #4c78a8; stroke-width: 2; opacity: 0.7;"></path></g></g><path class="foreground" d="" style="display: none; fill: none;"></path></g></g></g></svg>'

The second plot appears different from the first and the exported SVG of the second contains many NaNs

Ubuntu 16.04.6
python 3.7.7
altair 4.1.0
altair-data-server 0.4.1
altair-saver 0.5.0
altair-viewer 0.3.0
selenium 3.141.0

Saving SVG fails with Selenium InvalidArgumentException: Message: invalid argument: missing command parameters

I just made some changes to my app that had nothing to do with the code that saves charts, and somehow now SVG saving is broken with the exception below. PNG saves just fine. Seriously, I didn't touch the code for saving charts; the only way I can imagine my code changes affecting these code paths are that I added a bunch of library dependencies.

I'm using:

  • ChromeDriver 83.0.4103.116 (8f0c18b4dca9b6699eb629be0f51810c24fb6428-refs/branch-heads/4103@{#716})
  • Python Selenium package version 3.141.0
  • In fact, all the exact Python library dependencies are in this poetry.lock file

I'm enabling altair_saver renderers here, and my call into the Altair save method is here.

Stack trace:

Traceback (most recent call last):
  File "/usr/local/bin/covid19pr", line 8, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.7/site-packages/covid_19_puerto_rico/__init__.py", line 81, in main
    target.render(date_range)
  File "/usr/local/lib/python3.7/site-packages/covid_19_puerto_rico/charts.py", line 29, in render
    self.render_bulletin_date(df, bulletin_date)
  File "/usr/local/lib/python3.7/site-packages/covid_19_puerto_rico/charts.py", line 37, in render_bulletin_date
    self.output_formats)
  File "/usr/local/lib/python3.7/site-packages/covid_19_puerto_rico/util.py", line 42, in save_chart
    chart.save(filename)
  File "/usr/local/lib/python3.7/site-packages/altair/vegalite/v4/api.py", line 476, in save
    result = save(**kwds)
  File "/usr/local/lib/python3.7/site-packages/altair/utils/save.py", line 121, in save
    **kwargs,
  File "/usr/local/lib/python3.7/site-packages/altair/utils/mimebundle.py", line 60, in spec_to_mimebundle
    return altair_saver.render(spec, format, mode=mode, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/altair_saver/_core.py", line 257, in render
    mimebundle.update(saver.mimebundle(fmt))
  File "/usr/local/lib/python3.7/site-packages/altair_saver/savers/_saver.py", line 90, in mimebundle
    bundle[mimetype] = self._serialize(fmt, "mimebundle")
  File "/usr/local/lib/python3.7/site-packages/altair_saver/savers/_selenium.py", line 284, in _serialize
    out = self._extract(fmt)
  File "/usr/local/lib/python3.7/site-packages/altair_saver/savers/_selenium.py", line 278, in _extract
    result = driver.execute_async_script(EXTRACT_CODE, self._spec, opt, fmt)
  File "/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 659, in execute_async_script
    'args': converted_args})['value']
  File "/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
    self.error_handler.check_response(response)
  File "/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
    raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.InvalidArgumentException: Message: invalid argument: missing command parameters

Support HTML rendering with bundled assets (vs. CDN)

It appears that it's only possible to use bundled assets with the HTMLSaver in standalone mode. It would be useful to support being able to use local assets to support, e.g., Jupyter notebooks in an offline environment without a disk cache of the CDN assets.

I know this is possible with alt.renderers.enable("altair_viewer", inline=True), but is there a way currently to do this with altair_saver that I'm missing?

Different visualisation from Altair_saver

Hello,

I would like to show you a different visualization if you use Altair_saver

Here's my code

import altair as alt
from altair_saver import save
import numpy as np
import pandas as pd

alt.data_transformers.disable_max_rows()
condition = ['Dry', 'Wet']
RSDF9 = pd.DataFrame({
    'DAY': pd.date_range('1990-01-01', freq='D', periods=10000),
    'CONDITION_RR': np.random.choice(condition , 10000,)
})

bar3 = alt.Chart(RSDF9).mark_bar().encode(
    alt.X('monthdate(DAY):O', title='day', axis=alt.Axis(tickCount=5)),
    alt.Y('year(DAY):O', title='year'),
    alt.Color('CONDITION_RR:N',
              scale=alt.Scale(
              domain=['Dry', 'Wet'],
              range=['#d62728', '#1f77b4'])),
    order=alt.Order("CONDITION_RR", sort="ascending")
)
(bar3).properties(width=600, title="Dry and Wet season (1983 - 2019)").configure_axisX(labelAlign="right", labelAngle=-45, titlePadding=5)
save(bar3, 'development_dry_season.svg')

The manually downloaded image:
image

By Altair_saver:
image

Altair Themes Ignored in PDF Output

I've been debugging an issue where it seems like altair is not preserving the theme for PDF output (e.g., fivethirtyeight).

I poked around in a debug session and think I understand what is going on, but not certain.

For SVG/Json/Html/Png, the theme option is being respected since they use vega-embed (png seems to use selenium, so html which uses vega embed). However, the vega CLI tools don't seem to use vega embed and don't otherwise handle theming. Someone reported a similar issue on stackoverflow, but with the png CLI tool https://stackoverflow.com/questions/59973181/how-to-apply-a-vega-theme-when-using-vl2png

Any suggestions on a workaround within altair to fix this up? The post on SO seems like it would work, but is a bit inconvenient.

Support native PDF LaTeX export

Currently I use the inkscape -D -z --file input.svg --export-pdf output.pdf_tex --export-latex to create a PDF file that can be imported into LaTeX and uses the fonts etc. from LaTeX. But I have to manually escape f.e. the percentage signs from the axis or fiddle around quite a lot to make the paddings etc. work. Therefore I'd like to know, if a native latex PDF LaTeX export is on the table for the future.

PDF failure

Giving the package a run but failed to produce PDFs. The function runs without errors but the pdf produced is blank. I am on macOS 10.12.6

Is there a way I specify the backend or does it know to use nodejs when I ask for a PDF?

I ran npm install vega-lite vega-cli canvas which resulted in

npm WARN saveError ENOENT: no such file or directory, open '/Users/eitanlees/package.json'
npm WARN enoent ENOENT: no such file or directory, open '/Users/eitanlees/package.json'
npm WARN eitanlees No description
npm WARN eitanlees No repository field.
npm WARN eitanlees No README data
npm WARN eitanlees No license field.

+ [email protected]
+ [email protected]
+ [email protected]
updated 3 packages and audited 14777 packages in 4.667s

4 packages are looking for funding
  run `npm fund` for details

found 1 low severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details

I am unfamiliar with node so I don't know if that is what should happen.

Here is a small script I ran in a Jupyter notebook for reproducibility.

import altair as alt
from vega_datasets import data
from altair_saver import save

chart = alt.Chart(data.iris.url).mark_point().encode(
    alt.X('petalWidth:Q'), alt.Y('sepalLength:Q')
)

save(chart, 'test.pdf')

Connection refused error

I'm getting an issue that looks like this when I'm trying to save a relatively large chart. I get this error when using either the node backend or the selenium backend (correctly installed all dependencies according to the readme). What could cause this problem? This only happens on the call to save(summary_graph,'summary_graph.png') where summary_graph is of type altair.vegalite.v4.api.HConcatChart.

ConnectionRefusedError                    Traceback (most recent call last)
/usr/local/lib/python3.7/site-packages/urllib3/connection.py in _new_conn(self)
    156             conn = connection.create_connection(
--> 157                 (self._dns_host, self.port), self.timeout, **extra_kw
    158             )

/usr/local/lib/python3.7/site-packages/urllib3/util/connection.py in create_connection(address, timeout, source_address, socket_options)
     83     if err is not None:
---> 84         raise err
     85 

/usr/local/lib/python3.7/site-packages/urllib3/util/connection.py in create_connection(address, timeout, source_address, socket_options)
     73                 sock.bind(source_address)
---> 74             sock.connect(sa)
     75             return sock

ConnectionRefusedError: [Errno 61] Connection refused

During handling of the above exception, another exception occurred:

NewConnectionError                        Traceback (most recent call last)
/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    671                 headers=headers,
--> 672                 chunked=chunked,
    673             )

/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py in _make_request(self, conn, method, url, timeout, chunked, **httplib_request_kw)
    386         else:
--> 387             conn.request(method, url, **httplib_request_kw)
    388 

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py in request(self, method, url, body, headers, encode_chunked)
   1243         """Send a complete request to the server."""
-> 1244         self._send_request(method, url, body, headers, encode_chunked)
   1245 

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py in _send_request(self, method, url, body, headers, encode_chunked)
   1289             body = _encode(body, 'body')
-> 1290         self.endheaders(body, encode_chunked=encode_chunked)
   1291 

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py in endheaders(self, message_body, encode_chunked)
   1238             raise CannotSendHeader()
-> 1239         self._send_output(message_body, encode_chunked=encode_chunked)
   1240 

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py in _send_output(self, message_body, encode_chunked)
   1025         del self._buffer[:]
-> 1026         self.send(msg)
   1027 

/usr/local/Cellar/python/3.7.4_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py in send(self, data)
    965             if self.auto_open:
--> 966                 self.connect()
    967             else:

/usr/local/lib/python3.7/site-packages/urllib3/connection.py in connect(self)
    183     def connect(self):
--> 184         conn = self._new_conn()
    185         self._prepare_conn(conn)

/usr/local/lib/python3.7/site-packages/urllib3/connection.py in _new_conn(self)
    168             raise NewConnectionError(
--> 169                 self, "Failed to establish a new connection: %s" % e
    170             )

NewConnectionError: <urllib3.connection.HTTPConnection object at 0x147bf2a90>: Failed to establish a new connection: [Errno 61] Connection refused

During handling of the above exception, another exception occurred:

MaxRetryError                             Traceback (most recent call last)
<ipython-input-147-2a814c35573f> in <module>
----> 1 save(label_summary_graph,'summary_graph.png')

/usr/local/lib/python3.7/site-packages/altair_saver/_core.py in save(chart, fp, fmt, mode, method, **kwargs)

/usr/local/lib/python3.7/site-packages/altair_saver/savers/_saver.py in save(self, fp, fmt)

/usr/local/lib/python3.7/site-packages/altair_saver/savers/_saver.py in mimebundle(self, fmts)

/usr/local/lib/python3.7/site-packages/altair_saver/savers/_selenium.py in _mimebundle(self, fmt)

/usr/local/lib/python3.7/site-packages/altair_saver/savers/_selenium.py in _extract(self, fmt)

/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py in get(self, url)
    331         Loads a web page in the current browser session.
    332         """
--> 333         self.execute(Command.GET, {'url': url})
    334 
    335     @property

/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/webdriver.py in execute(self, driver_command, params)
    317 
    318         params = self._wrap_value(params)
--> 319         response = self.command_executor.execute(driver_command, params)
    320         if response:
    321             self.error_handler.check_response(response)

/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/remote_connection.py in execute(self, command, params)
    372         data = utils.dump_json(params)
    373         url = '%s%s' % (self._url, path)
--> 374         return self._request(command_info[0], url, body=data)
    375 
    376     def _request(self, method, url, body=None):

/usr/local/lib/python3.7/site-packages/selenium/webdriver/remote/remote_connection.py in _request(self, method, url, body)
    395 
    396         if self.keep_alive:
--> 397             resp = self._conn.request(method, url, body=body, headers=headers)
    398 
    399             statuscode = resp.status

/usr/local/lib/python3.7/site-packages/urllib3/request.py in request(self, method, url, fields, headers, **urlopen_kw)
     78         else:
     79             return self.request_encode_body(
---> 80                 method, url, fields=fields, headers=headers, **urlopen_kw
     81             )
     82 

/usr/local/lib/python3.7/site-packages/urllib3/request.py in request_encode_body(self, method, url, fields, headers, encode_multipart, multipart_boundary, **urlopen_kw)
    169         extra_kw.update(urlopen_kw)
    170 
--> 171         return self.urlopen(method, url, **extra_kw)

/usr/local/lib/python3.7/site-packages/urllib3/poolmanager.py in urlopen(self, method, url, redirect, **kw)
    328             response = conn.urlopen(method, url, **kw)
    329         else:
--> 330             response = conn.urlopen(method, u.request_uri, **kw)
    331 
    332         redirect_location = redirect and response.get_redirect_location()

/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    760                 release_conn=release_conn,
    761                 body_pos=body_pos,
--> 762                 **response_kw
    763             )
    764 

/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    760                 release_conn=release_conn,
    761                 body_pos=body_pos,
--> 762                 **response_kw
    763             )
    764 

/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    760                 release_conn=release_conn,
    761                 body_pos=body_pos,
--> 762                 **response_kw
    763             )
    764 

/usr/local/lib/python3.7/site-packages/urllib3/connectionpool.py in urlopen(self, method, url, body, headers, retries, redirect, assert_same_host, timeout, pool_timeout, release_conn, chunked, body_pos, **response_kw)
    718 
    719             retries = retries.increment(
--> 720                 method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
    721             )
    722             retries.sleep()

/usr/local/lib/python3.7/site-packages/urllib3/util/retry.py in increment(self, method, url, response, error, _pool, _stacktrace)
    434 
    435         if new_retry.is_exhausted():
--> 436             raise MaxRetryError(_pool, url, error or ResponseError(cause))
    437 
    438         log.debug("Incremented Retry for (url='%s'): %r", url, new_retry)

MaxRetryError: HTTPConnectionPool(host='127.0.0.1', port=61028): Max retries exceeded with url: /session/cd2aa6c78fe58e790b74bd08e7eb0653/url (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x147bf2a90>: Failed to establish a new connection: [Errno 61] Connection refused'))

altair is not able to save png, even with a proper geckodriver and fierfox instalation

Hi There,

I run python 3.8.2, altair 19.3.0, altair-saver 19.3.0, selenium 3.141.0, geckodriver 0.26.0, firefox 77.01, no chromedriver, no chrome browser.

I can run all the three examples from the selenium pip page. Everything works:
https://pypi.org/project/selenium/
So I think the error is not in my selenium, geckodriver, of firefox installation.

But when I try to chart.save('this_is_my.png') I get the following error:

WebDriverException: Message: 'chromedriver' executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home

Somehow altair does not get that I like to use geckodriver and Firefox, not chrome driver and Chrome.
I tried to look for some setting in the save command, where I explicitly can set firefox, but I think it doesn't exist. Also, I had a look at altair_saver/savers/_selenium.py
So far I am not become savvy enough to understand where the error is happening.
Any help appreciated.

Thank you, Elmar

inline rendering svg

for example to get an svg output in pygal, I would do this

    chart = pygal.Line(disable_xml_declaration=True, x_label_rotation=45)
    return chart.render(is_unicode=True)

what's the equivalent in altair so I can get an svg embedded string, not a file that I can easily save to the db for output later ?

Execute altair_saver with user apache

Hi, thank you very much for this amazing library.

I have a problem when I execute this line with user apache in my application in a docker container.

chart.save('chart.png', scale_factor=2.0)

I obtain this error:

ValueError: No enabled saver found that supports format='png'

I am using chromedriver and chromium package, I was search in the code and I find a possible reason, in this lines of the file altair_saver/savers/_selenium.py is added a header only for user root.

        # For linux/osx root user with Chrome, need to add --no-sandbox option, which
        # must come before the --headless option. Note: geteuid doesn't exist on windows.
        if (
            issubclass(webdriver_class, selenium.webdriver.Chrome)
            and hasattr(os, "geteuid")
            and os.geteuid() == 0
        ):
            webdriver_options.add_argument("--no-sandbox")

I change the code of the file and added this header for all user and my application works without problems.

Its posible remove this validation only for root user and added the header for all users?

Thanks a lot.

scale_factor in altair_saver?

I have been using altair_saver as an alternative to chart.save because I keep running into headless chrome timeouts when I am producing lots of charts. However, the plots are at a much lower resolution and have a grainy appearance using altair_saver.save compared to chart.save method in the altair library.

Is there a way to control the figure resolution using altair_saver?

SyntaxError when saving as png

I just tried saving a simple scatter plot as a png. I receive the following error:

/home/user/miniconda3/envs/track/lib/vega-cli/node_modules/vega-dataflow/build/vega-dataflow.js:43
  async function asyncCallback(df, callback) {
        ^^^^^^^^

SyntaxError: Unexpected token function
    at createScript (vm.js:56:10)
    at Object.runInThisContext (vm.js:97:10)
    at Module._compile (module.js:549:28)
    at Object.Module._extensions..js (module.js:586:10)
    at Module.load (module.js:494:32)
    at tryModuleLoad (module.js:453:12)
    at Function.Module._load (module.js:445:3)
    at Module.require (module.js:504:17)
    at require (internal/module.js:20:19)
    at Object.<anonymous> (/home/user/miniconda3/envs/track/lib/vega-cli/node_modules/vega/build/vega-node.js:6:20)

I installed Altair Saver using conda install altair_saver as instructed in the docs. I'm guessing it's somehow related to the version of Nodejs and the code using syntax that is not supported by that version.

If it's any help, when I run node --version I get v6.13.1.

What's the best way to render pngs in notebook

I'm wondering what the best way is to setup altair to always render pngs.

I'm working in jupyter-lab.

I want my notebooks to be readable on github by default.

At the moment, I'm using this little show method:

import tempfile
from IPython.display import Image

def show(chart):
    file = tempfile.mktemp(suffix='.png')
    chart.save(file)
    return Image(file)

Make tests run offline

Currently test cases use URL-based data, and fail when run offline.

Tests should do the sensible thing (not require network access wherever possible, and skip cleanly with no internet connection where not possible)

ValueError: No enabled saver found that supports format='svg'

Hello,

I am trying to generate an SVG figure, but I got an error. Could you please guide me how to fix this? Thank you

fig_glutathione_seed.save('fig_glutathione_seed.svg')

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-19-2aedd223090b> in <module>
----> 1 fig_glutathione_seed.save('fig_glutathione_seed.svg')

/usr/local/lib/python3.7/site-packages/altair/vegalite/v4/api.py in save(self, fp, format, override_data_transformer, scale_factor, vegalite_version, vega_version, vegaembed_version, **kwargs)
    474         if override_data_transformer:
    475             with data_transformers.disable_max_rows():
--> 476                 result = save(**kwds)
    477         else:
    478             result = save(**kwds)

/usr/local/lib/python3.7/site-packages/altair/utils/save.py in save(chart, fp, vega_version, vegaembed_version, format, mode, vegalite_version, embed_options, json_kwds, webdriver, scale_factor, **kwargs)
    119             webdriver=webdriver,
    120             scale_factor=scale_factor,
--> 121             **kwargs,
    122         )
    123         if format == "png":

/usr/local/lib/python3.7/site-packages/altair/utils/mimebundle.py in spec_to_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, **kwargs)
     58                 "see http://github.com/altair-viz/altair_saver/".format(fmt=format)
     59             )
---> 60         return altair_saver.render(spec, format, mode=mode, **kwargs)
     61     if format == "html":
     62         html = spec_to_html(

/usr/local/lib/python3.7/site-packages/altair_saver/_core.py in render(chart, fmts, mode, embed_options, method, **kwargs)
    253 
    254     for fmt in fmts:
--> 255         Saver = _select_saver(method, mode=mode, fmt=fmt)
    256         saver = Saver(spec, mode=mode, embed_options=embed_options, **kwargs)
    257         mimebundle.update(saver.mimebundle(fmt))

/usr/local/lib/python3.7/site-packages/altair_saver/_core.py in _select_saver(method, mode, fmt, fp)
     67             if s.enabled() and fmt in s.valid_formats[mode]:
     68                 return s
---> 69         raise ValueError(f"No enabled saver found that supports format={fmt!r}")
     70     else:
     71         raise ValueError(f"Unrecognized method: {method}")

ValueError: No enabled saver found that supports format='svg'

JupyterLab saving plots

It seems that altair is able to save plots out of the box using actions. Could this be leveraged to save the plots to png/svg programmatically?

Selenium/Geckodriver consumes all memory

Mostly wanted to say Thanks! I'm not sure I can troubleshoot it.

Altair_saver is much faster (~6x for me) than doing geckodriver on charts one at a time.

On Linux with Firefox, altair_saver with selenium/geckodriver steadily consumes memory while running through iterations (done with multiprocessing) and crashes. Switching to node with method eliminates memory consumption.

Unable to save chart

Hi,
I am saving svg's using altair_saver in my code, and up until a few minutes ago, everything was fine. But now I am getting the following error message:

No enabled saver found that supports format='svg'.

Is there anything broken?

Chart with arc mark (pie chart) using Selenium backend errors out

I am using 'master' and I fail to create PNG exports using Selenium using example data 'https://vega.github.io/vega-lite/examples/layer_arc_label.html'.

Note that the whole setup works perfectly for any other type of chart.

I am invoking altair_saver.save method with the following options {"method": "selenium", "scale_factor": 2.0}, and forcing output to PNG using a filename with a PNG extension, and I get the following error:

Traceback (most recent call last):
  File "./scripts/generate_chart.py", line 231, in <module>
    sys.exit(main())
  File "./scripts/generate_chart.py", line 145, in main
    package_name.save_chart(data_def, args.output_directory, "name")
  File "/Users/user/Workspace/package-name/package_name/__init__.py", line 49, in save_chart
    altair_saver.save(chart, **option_set)
  File "/Users/user/.virtualenvs/venv36/src/altair-saver/altair_saver/_core.py", line 172, in save
    return saver.save(fp=fp, fmt=fmt)
  File "/Users/user/.virtualenvs/venv36/src/altair-saver/altair_saver/savers/_saver.py", line 121, in save
    content = self._serialize(fmt, "save")
  File "/Users/user/.virtualenvs/venv36/src/altair-saver/altair_saver/savers/_selenium.py", line 284, in _serialize
    out = self._extract(fmt)
  File "/Users/user/.virtualenvs/venv36/src/altair-saver/altair_saver/savers/_selenium.py", line 280, in _extract
    raise JavascriptError(result["error"])
altair_saver.savers._selenium.JavascriptError: TypeError: Cannot read property 'orient' of undefined

The error pops up only with the selenium backend (node backend works alright, but resolution of the image if quite sub-optimal, hence the choice of the selenium backend)

I am using the following dependencies (requirements.txt file):

-e git+https://github.com/altair-viz/altair.git@81b40755c51b5821b078d36702b3cccadfc84071#egg=altair
-e git+https://github.com/altair-viz/altair_saver.git@3c8e1102776a7acd2c971354190a283c394d1cfd#egg=altair-saver
altair-data-server==0.4.1
altair-viewer==0.3.0
attrs==20.3.0
entrypoints==0.3
importlib-metadata==3.3.0
Jinja2==2.11.2
jsonschema==3.2.0
MarkupSafe==1.1.1
numpy==1.19.5
pandas==1.1.5
portpicker==1.3.1
pyrsistent==0.17.3
python-dateutil==2.8.1
pytz==2020.5
selenium==3.141.0
six==1.15.0
toolz==0.11.1
tornado==6.1
typing-extensions==3.7.4.3
urllib3==1.26.2
vega-datasets==0.9.0
vegascope==1.0.14
zipp==3.4.0

Selenium 4.3.0 has deprecated `find_element_by*()` methods

Hi,

Just filing this issue - doing

chart.save() results in this error:

File "scripts/visualize_questions.py", line 61, in <module>
[11](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:12)
    chart.save('images/topics.png',webdriver='firefox',scale_factor=2)
[12](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:13)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair/vegalite/v4/api.py", line 488, in save
[13](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:14)
    result = save(**kwds)
[14](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:15)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair/utils/save.py", line 116, in save
[15](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:16)
    mimebundle = spec_to_mimebundle(
[16](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:17)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair/utils/mimebundle.py", line 60, in spec_to_mimebundle
[17](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:18)
    return altair_saver.render(spec, format, mode=mode, **kwargs)
[18](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:19)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair_saver/_core.py", line 257, in render
[19](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:20)
    mimebundle.update(saver.mimebundle(fmt))
[20](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:21)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair_saver/savers/_saver.py", line 90, in mimebundle
[21](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:22)
    bundle[mimetype] = self._serialize(fmt, "mimebundle")
[22](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:23)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair_saver/savers/_selenium.py", line 284, in _serialize
[23](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:24)
    out = self._extract(fmt)
[24](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:25)
  File "/opt/hostedtoolcache/Python/3.8.13/x64/lib/python3.8/site-packages/altair_saver/savers/_selenium.py", line 267, in _extract
[25](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:26)
    driver.find_element_by_id("vis")
[26](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:27)
AttributeError: 'WebDriver' object has no attribute 'find_element_by_id'
[27](https://github.com/open-resources/physics_bank/runs/7099958015?check_suite_focus=true#step:6:28)
WARNING:tornado.access:404 GET /favicon.ico (127.0.0.1) 0.41ms

The important bit is:

AttributeError: 'WebDriver' object has no attribute 'find_element_by_id'

This seems to have shown up on StackOverflow as well and the fix seems relatively simple. Let me see if I can submit a PR with the fix.

Unable to save to png format under Windows 10 using node

Despite many attempts, I cannot seem to save charts in "png" format (really any format, but that's what I want) under Anaconda w/Windows 10. I have no issue on a Mac.

The last part of the error is always:

JSONDecodeError: Expecting value: line 2 column 1 (char 2)

A png file is created, but it appears to contain a command rather than its output.

# Test Case
import altair as alt
from altair_saver import save as alt_save
import numpy as np
import pandas as pd
# make simple data frame
test_data = pd.DataFrame.from_dict({ 'x': np.arange(0,10), 'y':np.arange(0,10)})
# make a chart with Altair
test_data_chart = alt.Chart(test_data).mark_line().encode(x=alt.X('x'),y=alt.Y('y'))
# display it (renders fine in notebook, using Chrome)
test_data_chart

# Test Case 1:
test_data_chart.save('testoutput.png')

---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
<ipython-input-29-2049dff4a23c> in <module>
      1 # Test Case 1:
----> 2 test_data_chart.save('testoutput.png')

C:\ProgramData\Anaconda3\envs\myenvname\lib\site-packages\altair\vegalite\v4\api.py in save(self, fp, format, override_data_transformer, scale_factor, vegalite_version, vega_version, vegaembed_version, **kwargs)
    474         if override_data_transformer:
    475             with data_transformers.disable_max_rows():
--> 476                 result = save(**kwds)
    477         else:
    478             result = save(**kwds)

C:\ProgramData\Anaconda3\envs\myenvname\lib\site-packages\altair\utils\save.py in save(chart, fp, vega_version, vegaembed_version, format, mode, vegalite_version, embed_options, json_kwds, webdriver, scale_factor, **kwargs)
    119             webdriver=webdriver,
    120             scale_factor=scale_factor,
--> 121             **kwargs,
    122         )
    123         if format == "png":

C:\ProgramData\Anaconda3\myenvname\lib\site-packages\altair\utils\mimebundle.py in spec_to_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, **kwargs)
     58                 "see http://github.com/altair-viz/altair_saver/".format(fmt=format)
     59             )
---> 60         return altair_saver.render(spec, format, mode=mode, **kwargs)
     61     if format == "html":
     62         html = spec_to_html(

C:\ProgramData\Anaconda3\myenvname\lib\site-packages\altair_saver\_core.py in render(chart, fmts, mode, embed_options, method, **kwargs)
    255         Saver = _select_saver(method, mode=mode, fmt=fmt)
    256         saver = Saver(spec, mode=mode, embed_options=embed_options, **kwargs)
--> 257         mimebundle.update(saver.mimebundle(fmt))
    258 
    259     return mimebundle

C:\ProgramData\Anaconda3\myenvname\lib\site-packages\altair_saver\savers\_saver.py in mimebundle(self, fmts)
     88                 vegalite_version=self._package_versions["vega-lite"],
     89             )
---> 90             bundle[mimetype] = self._serialize(fmt, "mimebundle")
     91         return bundle
     92 

C:\ProgramData\Anaconda3\myenvname\lib\site-packages\altair_saver\savers\_node.py in _serialize(self, fmt, content_type)
    112 
    113         if self._mode == "vega-lite":
--> 114             spec = self._vl2vg(spec)
    115 
    116         if fmt == "vega":

C:\ProgramData\Anaconda3\myenvname\lib\site-packages\altair_saver\savers\_node.py in _vl2vg(self, spec)
     66             [vl2vg], input=vl_json, stderr_filter=self._stderr_filter
     67         )
---> 68         return json.loads(vg_json)
     69 
     70     def _vg2png(self, spec: JSONDict) -> bytes:

C:\ProgramData\Anaconda3\myenvname\lib\json\__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    346             parse_int is None and parse_float is None and
    347             parse_constant is None and object_pairs_hook is None and not kw):
--> 348         return _default_decoder.decode(s)
    349     if cls is None:
    350         cls = JSONDecoder

C:\ProgramData\Anaconda3\envs\myenvname\lib\json\decoder.py in decode(self, s, _w)
    335 
    336         """
--> 337         obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338         end = _w(s, end).end()
    339         if end != len(s):

C:\ProgramData\Anaconda3\envs\myenvname\lib\json\decoder.py in raw_decode(self, s, idx)
    353             obj, end = self.scan_once(s, idx)
    354         except StopIteration as err:
--> 355             raise JSONDecodeError("Expecting value", s, err.value) from None
    356         return obj, end

JSONDecodeError: Expecting value: line 2 column 1 (char 2)

If I try specifying mode="vega", it succeeds and produces a file, but it just contains a command not an PNG file:

# Try vega
test_data_chart.save('testoutput.png',mode='vega')

# testout.png contains this text (not a png file):
C:\>node C:/ProgramData/Anaconda3/envs/myenvname\Library\share\vega-cli\node_modules\vega-cli\bin\vg2png ""

FWIW: Python 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 15:37:01) [MSC v.1916 64 bit (AMD64)] on win32

Running inside of a crontab

Whenever I am running my code inside of a cron script, I get this error:

Traceback (most recent call last):
  File "/home/dhuck/projects/notebooks/COVID/update.py", line 466, in <module>
    main()
  File "/home/dhuck/projects/notebooks/COVID/update.py", line 456, in main
    save(world, world_file)
  File "/home/dhuck/projects/notebooks/.env/lib/python3.6/site-packages/altair_saver/_core.py", line 157
+, in save
    Saver = _select_saver(method, mode=mode, fmt=fmt, fp=fp)
  File "/home/dhuck/projects/notebooks/.env/lib/python3.6/site-packages/altair_saver/_core.py", line 72,
+in _select_saver
    raise ValueError(f"No enabled saver found that supports format={fmt!r}")
ValueError: No enabled saver found that supports format='vega'

This runs fine whenever I run the script as a user. I have added export DISPLAY=:0; before my script as noted here to no avail. I am calling altair_saver like so:

world_file    = f'{PWD}/data/world_covid.vg.json'
save(world, world_file)

I attempted installing the nodeJS packages which resulted in a malformed chart.

Saving figure with interactive elements

Is it possible to export interactive features?
For testing I made a plot with tool tips on hover and saved to pdf and svg (via nodejs). The figure is saved without problems, but the tool tip functionality is lost.

Cant import

Sorry if its a stupid issue, but I cant import the package after installing. It asks me for a modole that should be installed with the conda instalation.

--------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
c:\Users\Harari\Documents\GitHub\MarketSimulation\V3_Economy\main.py in 
----> 1 from altair_saver import save

~\Anaconda3\lib\site-packages\altair_saver\__init__.py in 
      1 """Tools for saving altair charts"""
----> 2 from ._core import render, save
      3 from .savers import Saver, BasicSaver, HTMLSaver, NodeSaver, SeleniumSaver
      4 
      5 __version__ = "0.1.0"

~\Anaconda3\lib\site-packages\altair_saver\_core.py in 
      3 import altair as alt
      4 
----> 5 from altair_saver.savers import (
      6     Saver,
      7     BasicSaver,

~\Anaconda3\lib\site-packages\altair_saver\savers\__init__.py in 
      1 from ._saver import Saver
      2 from ._basic import BasicSaver
----> 3 from ._html import HTMLSaver
      4 from ._node import NodeSaver
      5 from ._selenium import SeleniumSaver

~\Anaconda3\lib\site-packages\altair_saver\savers\_html.py in 
      5 from altair_saver.savers import Saver
      6 from altair_saver._utils import JSONDict, Mimebundle, fmt_to_mimetype
----> 7 from altair_viewer import get_bundled_script
      8 
      9 HTML_TEMPLATE = """

~\Anaconda3\lib\site-packages\altair_viewer\__init__.py in 
      4 __all__ = ["ChartViewer", "display", "render", "show", "get_bundled_script"]
      5 
----> 6 from altair_viewer._viewer import ChartViewer
      7 from altair_viewer._scripts import get_bundled_script
      8 

~\Anaconda3\lib\site-packages\altair_viewer\_viewer.py in 
      6 
      7 import altair as alt
----> 8 from altair_data_server import Provider, Resource
      9 from altair_viewer._scripts import get_bundled_script
     10 from altair_viewer._event_provider import EventProvider, DataSource

~\Anaconda3\lib\site-packages\altair_data_server\__init__.py in 
      9 ]
     10 
---> 11 from ._altair_server import AltairDataServer, data_server, data_server_proxied
     12 from ._provide import Provider, Resource

~\Anaconda3\lib\site-packages\altair_data_server\_altair_server.py in 
      4 from urllib import parse
      5 
----> 6 from altair_data_server._provide import Provider, Resource
      7 from altair.utils.data import (
      8     _data_to_json_string,

~\Anaconda3\lib\site-packages\altair_data_server\_provide.py in 
     29 import tornado.wsgi
     30 
---> 31 from altair_data_server import _background_server
     32 
     33 

~\Anaconda3\lib\site-packages\altair_data_server\_background_server.py in 
     20 import threading
     21 
---> 22 import portpicker
     23 import tornado
     24 import tornado.web

ModuleNotFoundError: No module named 'portpicker'

Unable to save to png / svg with Selenium with chrome driver on Ubuntu 20.04 and Python 3.8.10

Hi everyone,

I am using selenium and I get a traceback when I try to save my chart to a png / svg file with chrome driver.

I get a selenium.common.exceptions.WebDriverException: Message: unknown error: DevToolsActivePort file doesn't exist error

The chart is successfully saved with geckodriver.

The drivers are in my virtualenv "bin" folder, so they are in the path.

My code is the following : https://gist.github.com/fconil/b75c8eef479bb205ff226a13f0aeac62

I noticed there were many DevToolsActivePort issues on Selenium : https://github.com/SeleniumHQ/selenium/issues?q=is%3Aissue+DevToolsActivePort+is%3Aclosed

Regards

Chart.save produces different colors between html, png, and svg, not using enabled theme

Hello, I'm using altair 4.0.1 and produce a chart using
alt.themes.enable('fivethirtyeight')

When I use chart.save('foo.html') it uses the colors and layout from the theme that looks something like this (cropped oddly due to data sensitivity)

image

But when I use chart.save('foo.png') or chart.save('foo.svg'), the colors and layout are from the default theme
image

I tried looking in the docs for changing the theme on a chart and for save and couldn't find how to specify the theme when saving pngs and svgs.

Add option for passing additional logging options to vega (e.g., vg2svg)

Currently, it doesn't appear possible to pass arguments to vg2svg and stderr is being written directly into a notebook environment by check_output_with_stderr. This also isn't suppressible as it's not a Python warning.

While it's possible subclass NodeSaver and vg2svg, well, it's a lot of work. As with embed_options, it'd be nice to be able to pass vega_cli_options or some similar list that would get passed through with the cmd into subprocess.run.

Ideally, this would be settable on alt.renderers.options as with embed_options (or maybe not, I'm just spitballing on the implementation).

Concretely, the issue motivating all of this is that when you have multiple renderers enabled, e.g., alt.renderers.enable("altair_saver", fmts=["html", "svg"]) you see warnings in your Jupyter notebook from the svg renderer shelling out to vg2svg (I think):

image

Fontconfig Error - Fonts fail to render

Hello,

I am having an issue with saving altair charts after doing an install with conda on a miniconda3 docker image.
The versions I am using are:
altair 4.1.0 and
altair_saver 0.5.0

This error is thrown when the chart is saved:
Fontconfig error: Cannot load default config file

The resulting png when using the Window Rank Example then contains square symbols instead of the expected characters.
test

I expect this is due to the fonts in this docker image being installed different than other systems, but other packages seem to be able to handle this and find the fonts which are indeed available in the underlying Debian Image.

This issue is not present when saving to html, but with png, svg and pdf.

Add conveniences for working with the data_server backend

Currenlty, to show Altair plots created with the data_server enabled in other environments such as JupyterBook, I put something like this in my notebooks to fall back to an svg representation of the plot:

alt.renderers.enable('altair_saver', fmts=['vega-lite', 'svg'])

The downside with this is that it makes subsequent plots static, even if I switch from the data_server transformer to the default one and I need to manually change the renderer back with alt.renderers.enable('default') or alt.renderers.enable('altair_saver', fmts=['vega-lite', 'svg']) to show these plots.

I think it would be neat with a renderer format that inserts an html blurb only if the data_server is not activated (by checking alt.data_transformers.active and otherwise uses another format (could be called something like html_data_server). Even better would be if the default html format could somehow detect if there is not a live Python kernel running and if data_server was used, and if both those are true, remove the HTML spec instead of showing the empty chart)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.