Skip to content
This repository was archived by the owner on Apr 9, 2024. It is now read-only.

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

Closed
elmbeech opened this issue Jun 6, 2020 · 11 comments

Comments

@elmbeech
Copy link

elmbeech commented Jun 6, 2020

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

@elmbeech
Copy link
Author

elmbeech commented Jun 6, 2020

This is the whole trackback:

In [1]: ich.save('ich.png')                                                 
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
~/.local/lib/python3.8/site-packages/selenium/webdriver/common/service.py in start(self)
     71             cmd.extend(self.command_line_args())
---> 72             self.process = subprocess.Popen(cmd, env=self.env,
     73                                             close_fds=platform.system() != 'Windows',

/usr/lib/python3.8/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)
    853 
--> 854             self._execute_child(args, executable, preexec_fn, close_fds,
    855                                 pass_fds, cwd, env,

/usr/lib/python3.8/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1701                         err_msg = os.strerror(errno_num)
-> 1702                     raise child_exception_type(errno_num, err_msg, err_filename)
   1703                 raise child_exception_type(err_msg)

FileNotFoundError: [Errno 2] No such file or directory: 'chromedriver'

During handling of the above exception, another exception occurred:

WebDriverException                        Traceback (most recent call last)
<ipython-input-7-a761e5980e90> in <module>
----> 1 ich.save('ich.png')

~/.local/lib/python3.8/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)

~/.local/lib/python3.8/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,

~/.local/lib/python3.8/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(

~/.local/lib/python3.8/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

~/.local/lib/python3.8/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 

~/.local/lib/python3.8/site-packages/altair_saver/savers/_selenium.py in _serialize(self, fmt, content_type)
    282 
    283     def _serialize(self, fmt: str, content_type: str) -> MimebundleContent:
--> 284         out = self._extract(fmt)
    285         if fmt == "png":
    286             assert isinstance(out, str)

~/.local/lib/python3.8/site-packages/altair_saver/savers/_selenium.py in _extract(self, fmt)
    230 
    231     def _extract(self, fmt: str) -> MimebundleContent:
--> 232         driver = self._registry.get(self._webdriver, self._driver_timeout)
    233 
    234         if self._offline:

~/.local/lib/python3.8/site-packages/altair_saver/savers/_selenium.py in get(self, webdriver, driver_timeout)
    139         webdriver_options.add_argument("--headless")
    140 
--> 141         driver_obj = webdriver_class(options=webdriver_options)
    142         atexit.register(driver_obj.quit)
    143         driver_obj.set_page_load_timeout(driver_timeout)

~/.local/lib/python3.8/site-packages/selenium/webdriver/chrome/webdriver.py in __init__(self, executable_path, port, options, service_args, desired_capabilities, service_log_path, chrome_options, keep_alive)
     71             service_args=service_args,
     72             log_path=service_log_path)
---> 73         self.service.start()
     74 
     75         try:

~/.local/lib/python3.8/site-packages/selenium/webdriver/common/service.py in start(self)
     79         except OSError as err:
     80             if err.errno == errno.ENOENT:
---> 81                 raise WebDriverException(
     82                     "'%s' executable needs to be in PATH. %s" % (
     83                         os.path.basename(self.path), self.start_error_message)

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

@jakevdp
Copy link
Member

jakevdp commented Jun 6, 2020

Try like this:

chart.save('chart.png', webdriver='firefox')

@elmbeech
Copy link
Author

elmbeech commented Jun 6, 2020

Wow, Jake!
Thank you for this quick response.

This solves the problem!
The png gets nicely generated.

Additionally, I get a page not found warning, but I am sure this not from altair, although maybe it is of interest: WARNING:tornado.access:404 GET /favicon.ico (::1) 0.31ms

Thank you! Would it be helpful when I update docstring or documentation with the webdriver parameter information?
I looked again in the code, but I can not find out where it really happens, where altair-saver detects which exact driver it really is.
Elmar

@Midnighter
Copy link

@jakevdp can this be configured globally somehow? I'm running altair in Rmarkdown which uses reticulate and saves to PNG automatically so I think I don't have control over how images are saved.

@Midnighter
Copy link

I tried with a custom renderer but that gets circumvented by a direct call in spec_to_mimebundle.

My attempt at a custom renderer:

from altair_saver import render

def custom_altair_renderer(spec, **kwargs):
    return render(spec, webdriver="firefox", **kwargs)

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

Traceback from knitting the Rmarkdown to markdown.

Detailed traceback:
  File "~/.pyenv/versions/3.8.10/envs/biotech/lib/python3.8/site-packages/altair/vegalite/v4/api.py", line 476, in save
    result = save(**kwds)
  File "~/.pyenv/versions/3.8.10/envs/biotech/lib/python3.8/site-packages/altair/utils/save.py", line 112, in save
    mimebundle = spec_to_mimebundle(
  File "~/.pyenv/versions/3.8.10/envs/biotech/lib/python3.8/site-packages/altair/utils/mimebundle.py", line 60, in spec_to_mimebundle
    return altair_saver.render(spec, format, mode=mode, **kwargs)
  File "~/.pyenv/versions/3.8.10/envs/biotech/lib/python3.8/site-packages/altair_saver/_core.py", line 257, in render
    mimebundle.update(saver.mimebundle(fmt))
  File "~/.pyenv/versions/3.8.10/envs/biotech/lib/python3.8/site-packages/altair_saver/savers/_saver.py", l
Calls: source ... eng_python_autoprint -> <Anonymous> -> py_call_impl

@Midnighter
Copy link

I now see that something goes wrong in the selenium driver selection. There is a geckodriver.log file with the following contents:

1621068840324	geckodriver	INFO	Listening on 127.0.0.1:47499
1621068841332	mozrunner::runner	INFO	Running command: "/usr/bin/firefox" "--marionette" "--headless" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofileo0zlFr"
*** You are running in headless mode.
1621068841809	Marionette	INFO	Marionette enabled

(/usr/lib/firefox/firefox:38513): GLib-GObject-CRITICAL **: 10:54:02.235: g_object_set: assertion 'G_IS_OBJECT (object)' failed

(/usr/lib/firefox/firefox:38533): GLib-GObject-CRITICAL **: 10:54:02.373: g_object_set: assertion 'G_IS_OBJECT (object)' failed
console.warn: SearchSettings: "get: No settings file exists, new profile?" (new Error("", "(unknown module)"))
1621068842978	Marionette	INFO	Listening on port 38903
1621068843087	Marionette	WARN	TLS certificate errors will be ignored for this session

(/usr/lib/firefox/firefox:38610): GLib-GObject-CRITICAL **: 10:54:03.205: g_object_set: assertion 'G_IS_OBJECT (object)' failed

but cls._registry.get(driver, driver_timeout) in SeleniumSaver does not return an exception 🤔

So the final message I see is just

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

which initially made me believe that the geckodriver was not being looked for.

@Midnighter
Copy link

I have very liberally added print calls and found the following:

In the _core.render function, the call to _select_saver in its call to Saver.enabled correctly selects the SeleniumSaver and in the enabled class method the call to cls._select_webdriver(20) correctly chooses 'firefox'. However, for whatever reason the render function already gets called with "chrome" as the webdriver argument. This comes from altair.utils.save.save which has "chrome" as a default argument. That seems to defeat the purpose of the driver selection method provided by altair_saver.

@Midnighter
Copy link

And indeed if I instead set the default argument to None it works as expected (in my case).

@jakevdp
Copy link
Member

jakevdp commented May 15, 2021

Thanks for digging into the issue – I've never actually tried altair_saver with geckodriver; I think there's probably room for improvement in how the drivers are configured and detected. In particular, there are some try...except blocks that do the heavy lifting and end up hiding errors that would be better to surface to the user.

@saiwing-yeung
Copy link

I ran into a similar issue when I was trying to save in a Jupyter notebook launched from papermill. Eventually I realized that altair_saver couldn't find my chromedriver, so I solved it by adding this:

os.environ['PATH'] += os.pathsep + '/usr/local/bin/'

So maybe OP needs to add the path to geckodriver.

mattijn added a commit to vega/altair that referenced this issue Dec 28, 2022
* fix: remove webdriver default argument to save

Fix altair-viz/altair_saver#72

* style: apply latest black formatting

Co-authored-by: Mattijn van Hoek <mattijn@gmail.com>
@joelostblom
Copy link
Member

Closed in vega/altair#2466

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants