vega / vl-convert

Utilities for converting Vega-Lite specs from the command line and Python
BSD 3-Clause "New" or "Revised" License
96 stars 12 forks source link

Valid spec using `labelExpr` and `tickBand` results in `ValueError: Vega to SVG conversion failed` #72

Closed RobinL closed 1 year ago

RobinL commented 1 year ago

Hello. Thank you for this wonderful tool. I'm the author of a library which makes extensive use of Altair charts (though note the Altair specs are actually stored as vega-lite specs and loaded into Altair using alt.Chart().from_dict)

vl-convert-python is working flawlessly on all of our charts except one. The spec of our chart is complex but I've managed to boil it down to the following reprex: open the Chart in the Vega Editor

Note this has: image

Expand me for full json spec ```json { "$schema": "https://vega.github.io/schema/vega-lite/v5.9.3.json", "data": { "values": [ { "column_name": "Prior", "previous_sum": 1.2, "log2_bayes_factor": 2.4, "value": "inal score" } ] }, "layer": [ { "mark": { "type": "bar", "width": 60 }, "encoding": { "x": { "type": "nominal", "axis": { "labelExpr": "datum.value == 'Prior' || datum.value == 'Final score' ? '' : datum.value", "tickBand": "extent" }, "field": "column_name" }, "y": { "type": "quantitative", "field": "previous_sum" } } } ] } ```
import altair as alt
import json

with open("replicate_vl.json") as f:
    data = json.load(f)

chart = alt.Chart().from_dict(data)
chart.save("delete.png")

This results in the following error:

ValueError: Vega to SVG conversion failed:
TypeError: Cannot read properties of undefined (reading 'marktype')
full error ``` ERROR Error: Failed to deserialize text info: missing field `text` at line 5 column 1 at Object.sg.textMetrics.width (:17:45) at Ve (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:1:30235) at J (https://cdn.skypack.dev/-/vega-view-transforms@v4.5.9-LiB26zIbxiHZW70fnrDI/dist=es2020,mode=imports,min/optimized/vega-view-transforms.js:1:1588) at https://cdn.skypack.dev/-/vega-view-transforms@v4.5.9-LiB26zIbxiHZW70fnrDI/dist=es2020,mode=imports,min/optimized/vega-view-transforms.js:1:1278 at Array.forEach () at Mt.transform (https://cdn.skypack.dev/-/vega-view-transforms@v4.5.9-LiB26zIbxiHZW70fnrDI/dist=es2020,mode=imports,min/optimized/vega-view-transforms.js:1:1259) at Mt.evaluate (https://cdn.skypack.dev/-/vega-dataflow@v5.7.5-asKYS4gpPLMPf64pSozt/dist=es2020,mode=imports,min/optimized/vega-dataflow.js:1:15429) at Mt.run (https://cdn.skypack.dev/-/vega-dataflow@v5.7.5-asKYS4gpPLMPf64pSozt/dist=es2020,mode=imports,min/optimized/vega-dataflow.js:1:15286) at ne.Gt [as evaluate] (https://cdn.skypack.dev/-/vega-dataflow@v5.7.5-asKYS4gpPLMPf64pSozt/dist=es2020,mode=imports,min/optimized/vega-dataflow.js:1:12073) at ne.evaluate (https://cdn.skypack.dev/-/vega-view@v5.11.1-FQ9r1BvJOMHegkomXDyj/dist=es2020,mode=imports,min/optimized/vega-view.js:2:1621) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) [/Users/robinlinacre/Documents/data_linking/splink/try_replicate_vl.py](https://file+.vscode-resource.vscode-cdn.net/Users/robinlinacre/Documents/data_linking/splink/try_replicate_vl.py) in line 8 [6](file:///Users/robinlinacre/Documents/data_linking/splink/try_replicate_vl.py?line=5) chart = alt.Chart().from_dict(data) [7](file:///Users/robinlinacre/Documents/data_linking/splink/try_replicate_vl.py?line=6) chart ----> [8](file:///Users/robinlinacre/Documents/data_linking/splink/try_replicate_vl.py?line=7) chart.save("delete.png") File [~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py:948](https://file+.vscode-resource.vscode-cdn.net/Users/robinlinacre/Documents/data_linking/splink/~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py:948), in TopLevelMixin.save(self, fp, format, override_data_transformer, scale_factor, vegalite_version, vega_version, vegaembed_version, **kwargs) [946](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py?line=945) if override_data_transformer: [947](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py?line=946) with data_transformers.disable_max_rows(): --> [948](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py?line=947) result = save(**kwds) [949](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py?line=948) else: [950](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/vegalite/v5/api.py?line=949) result = save(**kwds) File [~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py:154](https://file+.vscode-resource.vscode-cdn.net/Users/robinlinacre/Documents/data_linking/splink/~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py:154), in save(chart, fp, vega_version, vegaembed_version, format, mode, vegalite_version, embed_options, json_kwds, webdriver, scale_factor, engine, inline, **kwargs) [152](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=151) write_file_or_filename(fp, mimebundle["text/html"], mode="w") [153](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=152) elif format in ["png", "svg", "pdf", "vega"]: --> [154](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=153) mimebundle = spec_to_mimebundle( [155](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=154) spec=spec, [156](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=155) format=format, [157](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=156) mode=mode, [158](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=157) vega_version=vega_version, [159](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=158) vegalite_version=vegalite_version, [160](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=159) vegaembed_version=vegaembed_version, [161](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=160) webdriver=webdriver, [162](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=161) scale_factor=scale_factor, [163](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=162) engine=engine, [164](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=163) **kwargs, [165](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=164) ) [166](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=165) if format == "png": [167](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/save.py?line=166) write_file_or_filename(fp, mimebundle["image/png"], mode="wb") File [~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py:51](https://file+.vscode-resource.vscode-cdn.net/Users/robinlinacre/Documents/data_linking/splink/~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py:51), in spec_to_mimebundle(spec, format, mode, vega_version, vegaembed_version, vegalite_version, engine, **kwargs) [48](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=47) raise ValueError("mode must be 'vega-lite'") [50](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=49) if format in ["png", "svg", "pdf", "vega"]: ---> [51](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=50) return _spec_to_mimebundle_with_engine( [52](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=51) spec, format, mode, engine=engine, **kwargs [53](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=52) ) [54](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=53) if format == "html": [55](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=54) html = spec_to_html( [56](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=55) spec, [57](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=56) mode=mode, (...) [61](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=60) **kwargs, [62](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=61) ) File [~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py:111](https://file+.vscode-resource.vscode-cdn.net/Users/robinlinacre/Documents/data_linking/splink/~/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py:111), in _spec_to_mimebundle_with_engine(spec, format, mode, **kwargs) [109](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=108) return {"image/svg+xml": svg} [110](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=109) elif format == "png": --> [111](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=110) png = vlc.vegalite_to_png( [112](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=111) spec, [113](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=112) vl_version=vl_version, [114](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=113) scale=kwargs.get("scale_factor", 1.0), [115](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=114) ) [116](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=115) return {"image/png": png} [117](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=116) else: [118](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=117) # This should be validated above [119](file:///Users/robinlinacre/Documents/data_linking/splink/.venv/lib/python3.8/site-packages/altair/utils/mimebundle.py?line=118) # but raise exception for the sake of future development ValueError: Vega to SVG conversion failed: TypeError: Cannot read properties of undefined (reading 'marktype') at sn.mark (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:2:1445) at https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:2:2054 at L (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:1:19746) at o (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:2:2042) at L (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:1:19746) at sn.mark (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:2:2323) at https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:2:2054 at L (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:1:19746) at o (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:2:2042) at L (https://cdn.skypack.dev/-/vega-scenegraph@v4.10.2-W1dltMWN7mO3TARazJA1/dist=es2020,mode=imports,min/optimized/vega-scenegraph.js:1:19746) ```

I'm using:

vl-convert-python==0.10.2
altair==5.0.1

and I'm on Python 3.8.3 on Mac OS Catalina (though I note it was one of our users who originally reported this problem, who's using Windows)

I note that if I delete either labelExpr or tickBand, the error no longer occurs. i.e. it only occurs when both are included in the spec. Apologies if I've made a mistake and the error is my own!

jonmmease commented 1 year ago

Thanks for the report and helpful repro @RobinL. Also glad to hear that vl-convert is working well for you otherwise!

A fix is in progress in https://github.com/vega/vl-convert/pull/73 and I was able to use your repro as a test case.

How blocking is this issue for you and your users? Asking because we just released 0.11.0 yesterday, and I'm weighing how soon to cut another release with this fix.

RobinL commented 1 year ago

Amazing! Thanks so much. It's not a major blocker - it's more an edge case - so totally fine to release whenever is most convenient

jonmmease commented 1 year ago

Ok, sounds good. In that case we'll wait a week or so before cutting 0.11.1 (unless regressions are reported in 0.11.0).

jonmmease commented 1 year ago

0.11.1 was just released

RobinL commented 1 year ago

@jonmmease Thanks again! I've just tested it, and I can confirm this fixes the issue, including on the more complex spec we're using in Splink.