Altair: save PNG/SVG in Windows

Created on 25 Mar 2018  Â·  21Comments  Â·  Source: altair-viz/altair

I encountered a problem saving PNG/SVG in Windows. I installed Selenium by
conda install selenium
then downloaded chromedriver and put it under the system PATH variable (I think this is working). When I do chart.savechart('test.png'), there's problem message as follows:

---------------------------------------------------------------------------
PermissionError                           Traceback (most recent call last)
<ipython-input-16-c16ecf6573c1> in <module>()
----> 1 chart.savechart('test.png')

G:\Software\Anaconda2\envs\py36\lib\site-packages\altair\vegalite\v2\api.py in savechart(self, fp, format, **kwargs)
    331             utils.write_file_or_filename(fp, spec_html, mode='w')
    332         elif format in ['png', 'svg']:
--> 333             utils.save_spec(self.to_dict(), fp, format=format, **kwargs)
    334         else:
    335             raise ValueError("unrecognized format: '{0}'".format(format))

G:\Software\Anaconda2\envs\py36\lib\site-packages\altair\utils\headless.py in save_spec(spec, fp, mode, format, driver_timeout)
    136                                                  spec, mode)
    137         finally:
--> 138             os.remove(htmlfile)
    139     finally:
    140         driver.close()

PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\PAGPI_~1\\AppData\\Local\\Temp\\tmpbyp9uhvp.html'

I suppose the temp html file is somehow being used in Windows so it's better to remove it after the chromedriver is closed? Let me know if you guys need further testing under Win environment

bug

All 21 comments

Ah, I vaguely remember running into this problem several years ago when trying to use NamedTemporaryFile with windows in a similar context in some other project... it's something to do with Windows having some sort of lock on temporary files when they're used with a context manager. I think I ended up getting around it by writing a custom named temporary file routine from scratch.

Windows... always trying to be special.

In the meantime, you can open figures in the browser and click "save to png" on the rendering.

I don't have access to a windows machine to debug this, so if anyone else has time to take a look I'd appreciate it!

Found it... this is a "fix" for essentially the same bug when it came up in another project: https://github.com/jakevdp/JSAnimation/commit/7eab0c23e237df707f02cfff3606eee8867e8665

We'd want to do something more sophisticated than that, though (there's a reason I never released that other package :smile: )

And just for good measure, here's the relevant issue in CPython: https://bugs.python.org/issue14243 (looks like this won't be a problem any more in Python 3.7)

Thanks Jake! I tried your 'more sophisticated version' and it now worked. Do you want to adopt this solution? If so, I can do a PR

I ended up opening #650... it's a slightly lighter-weight solution.

Can you try that and see if it works?

I got this error message (see following) using the 2.0.0dev version, seems due to chromedriver? (But I've got chromedriver work in the 2.0.0rc1 with the _NameOnlyTemporaryFile hack.)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
G:\Software\Anaconda2\envs\test_env\lib\site-packages\altair\utils\headless.py in spec_to_image_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, driver_timeout)
    142         chrome_options.add_argument("--headless")
--> 143         if os.geteuid() == 0:
    144             chrome_options.add_argument('--no-sandbox')

AttributeError: module 'os' has no attribute 'geteuid'

During handling of the above exception, another exception occurred:

UnboundLocalError                         Traceback (most recent call last)
<ipython-input-7-343303c31e8b> in <module>()
----> 1 chart.savechart('test_fixtempfile.svg')

G:\Software\Anaconda2\envs\test_env\lib\site-packages\altair\vegalite\v2\api.py in savechart(self, fp, format, **kwargs)
    368             DeprecationWarning
    369         )
--> 370         return self.save(fp, format=None, **kwargs)
    371 
    372     def save(self, fp, format=None, **kwargs):

G:\Software\Anaconda2\envs\test_env\lib\site-packages\altair\vegalite\v2\api.py in save(self, fp, format, **kwargs)
    391                          vega_version=VEGA_VERSION,
    392                          vegaembed_version=VEGAEMBED_VERSION,
--> 393                          **kwargs)
    394 
    395     # Layering and stacking

G:\Software\Anaconda2\envs\test_env\lib\site-packages\altair\utils\save.py in save(chart, fp, vega_version, vegaembed_version, format, mode, vegalite_version, opt, json_kwds)
    126                                               vega_version=vega_version,
    127                                               vegalite_version=vegalite_version,
--> 128                                               vegaembed_version=vegaembed_version)
    129         if format == 'png':
    130             write_file_or_filename(fp, mimebundle['image/png'], mode='wb')

G:\Software\Anaconda2\envs\test_env\lib\site-packages\altair\utils\headless.py in spec_to_image_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, driver_timeout)
    160             os.remove(htmlfile)
    161     finally:
--> 162         driver.close()
    163 
    164     if format == 'png':

UnboundLocalError: local variable 'driver' referenced before assignment

Hmm... that's a change that @elyase added so that they could make it work with chromedriver on root. I didn't realize that the existence of the geteuid function is OS-dependent.

@elyase, how difficult would it be to make this check work on Windows?

Turned out os.geteuid() is invalid in Windows. I changed that to os.getpid() and solved this, but still get

PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\PAGPI_~1\\AppData\\Local\\Temp\\tmpnguk1px7.html'

Makes sense. We'd have to check with @elyase if changing to getpid still solves their problem.

Regarding the permission error: nothing has changed with respect to that on master. Please try running the code at #650 and let me know if that fixes your problem.

Sorry my bad. Yap, now it works great! Thanks man!

OK – I'll merge that and keep this open to figure out the geteuid thing.

To be honest, I don't quite understand what exactly that line accomplishes, so I'm not certain how to modify it to work properly on windows.

Hi all,
I'm trying to use Altair in Windows 10, Atom1.25+Hydrogen, Python 3.6.
I can see the figures using Renderer(cars) but cannot save them to png with cars.savechart('cars.png')

I installed selenium and added chromedriver.exe in C:\Windows but I get the error FileNotFoundError: [WinError 2] The system cannot find the file specified.

First question: which is the "file specified"?
As you see in the pics, cited files are there...
api

node

subprocess


FileNotFoundError Traceback (most recent call last)
in ()
----> 1 cars.savechart('cars.png')

C:\Program Files\Python\Python36-32\lib\site-packages\altair\v1\api.py in savechart(self, outfile, filetype)
224
225 if filetype in node.SUPPORTED_FILETYPES:
--> 226 node.savechart(self, outfile, filetype)
227 elif filetype == 'json':
228 if hasattr(outfile, 'write'):

C:\Program Files\Python\Python36-32\lib\site-packages\altair\utilsnode.py in savechart(chart, filename, filetype, verbose)
138 'vl2' + filetype,
139 outfile=filename,
--> 140 verbose=verbose)

C:\Program Files\Python\Python36-32\lib\site-packages\altair\utilsnode.py in _convert_vegalite_spec(spec, cmd, outfile, verbose)
75 if verbose:
76 print('> ' + ' '.join(cmd))
---> 77 p = Popen([cmd], stdout=PIPE, stdin=PIPE, stderr=PIPE)
78
79 input_ = '{0}'.format(json.dumps(spec))

C:\Program Files\Python\Python36-32\lib\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)
707 c2pread, c2pwrite,
708 errread, errwrite,
--> 709 restore_signals, start_new_session)
710 except:
711 # Cleanup if the child failed starting.

C:\Program Files\Python\Python36-32\lib\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, unused_restore_signals, unused_start_new_session)
995 env,
996 os.fspath(cwd) if cwd is not None else None,
--> 997 startupinfo)
998 finally:
999 # Child is launched. Close the parent's copy of those pipe

FileNotFoundError: [WinError 2] The system cannot find the file specified

It looks like you're using an old version of Altair (node.py listed in your path is no longer part of the library).

Install Altair version 2.0 following the instructions here and go from there.

Ok, updated. Now I get the same permission error as above PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\Users\PAGPI_~1\AppData\Local\Temp\tmpbyp9uhvp.html'

I copy/paste the code here in headless.py

but now I have the following error


ImportError Traceback (most recent call last)
in ()
----> 1 import altair.vegalite.v2 as alt

C:\Program Files\Python\Python36-32\lib\site-packages\altair__init__.py in ()
1 __version__ = '2.0.0rc1'
2
----> 3 from .vegalite import *

C:\Program Files\Python\Python36-32\lib\site-packages\altair\vegalite__init__.py in ()
----> 1 from .v2 import *

C:\Program Files\Python\Python36-32\lib\site-packages\altair\vegalite\v2__init__.py in ()
----> 1 from .schema import *
2 from .api import *
3
4 from . import examples
5

C:\Program Files\Python\Python36-32\lib\site-packages\altair\vegalite\v2\schema__init__.py in ()
----> 1 from .core import *
2 from .channels import *
3 SCHEMA_VERSION = 'v2.3.0'
4 SCHEMA_URL = 'https://vega.github.io/schema/vega-lite/v2.3.0.json'

C:\Program Files\Python\Python36-32\lib\site-packages\altair\vegalite\v2\schema\core.py in ()
2 # tools/generate_schema_wrapper.py. Do not modify directly.
3
----> 4 from altair.utils.schemapi import SchemaBase, Undefined
5
6 import os

C:\Program Files\Python\Python36-32\lib\site-packages\altair\utils__init__.py in ()
5 from .plugin_registry import PluginRegistry
6
----> 7 from .headless import save_spec

ImportError: cannot import name 'save_spec'

The current version will not have from .headless import save_spec line. It's possible that somehow different versions got mixed. Maybe try to pip uninstall altair then reinstall it. The best way to test this is to create a new env and see if things work.

I think you're probably hitting this bug: https://github.com/altair-viz/altair/issues/649

The fix is in master, but not yet in any release. If you install the master version I think it should fix your issue.

pip install git+git://github.com/altair-viz/altair.git

Finally! Thanks for help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

morberg picture morberg  Â·  3Comments

jtbaker picture jtbaker  Â·  3Comments

mroswell picture mroswell  Â·  4Comments

maxgerma picture maxgerma  Â·  3Comments

nielsmde picture nielsmde  Â·  4Comments